From e2ed6d83cede6b9403c62324c3bfdc1fed31ba0b Mon Sep 17 00:00:00 2001 From: Daniel Chalmers Date: Fri, 17 Jul 2026 15:31:58 -0500 Subject: [PATCH 1/3] Polish MD3E details: icon sizing, splash colors, mood grey, layout stability - Shrink the launcher glyph to the Material 52dp keyline and rebalance the monochrome inset - Let Android 12+ show the real launcher icon on the splash and theme the splash background to match the app and loading screen - Fix the dark-mode white flash by theming the page background and making the WebView transparent - Use a neutral grey for the straight-face mood in the calendar - Drop segmented-control checkmarks so long labels fit at large font scales - Keep the sleep readout a fixed width so the slider doesn't shift - Fade in a level-1 shadow under the sticky header on scroll - Mention Material You, M3E theming, and haptics in the README --- JournalApp/Components/DataPointView.razor | 8 +++---- JournalApp/Components/DataPointView.razor.css | 7 ++++++ JournalApp/Data/PreferenceService.cs | 7 +++--- JournalApp/MainPage.xaml | 9 ++++---- JournalApp/MainPage.xaml.cs | 14 +++++++++++- JournalApp/Pages/SettingsPage.razor | 2 +- .../Resources/drawable/appicon_monochrome.xml | 10 ++++----- .../Resources/drawable/splash_screen.xml | 9 ++++++++ .../Android/Resources/values-night/colors.xml | 3 ++- .../Android/Resources/values-v31/styles.xml | 2 +- .../Android/Resources/values/colors.xml | 5 ++++- JournalApp/Resources/AppIcon/appiconfg.svg | 2 +- .../Resources/AppIcon/appiconfg_debug.svg | 2 +- JournalApp/wwwroot/app.css | 22 ++++++++++++++++--- README.md | 4 +++- 15 files changed, 79 insertions(+), 27 deletions(-) diff --git a/JournalApp/Components/DataPointView.razor b/JournalApp/Components/DataPointView.razor index 77187887..63e455b8 100644 --- a/JournalApp/Components/DataPointView.razor +++ b/JournalApp/Components/DataPointView.razor @@ -25,7 +25,7 @@ else if (Point.Type == PointType.Scale) } else if (Point.Type == PointType.LowToHigh) { - + None Low Medium @@ -34,7 +34,7 @@ else if (Point.Type == PointType.LowToHigh) } else if (Point.Type == PointType.MildToSevere) { - + None Mild Moderate @@ -43,7 +43,7 @@ else if (Point.Type == PointType.MildToSevere) } else if (Point.Type == PointType.Bool) { - + No Yes @@ -75,7 +75,7 @@ else if (Point.Type == PointType.Note) else if (Point.Type == PointType.Medication) {
- + No Yes diff --git a/JournalApp/Components/DataPointView.razor.css b/JournalApp/Components/DataPointView.razor.css index 0ba5c066..7b902370 100644 --- a/JournalApp/Components/DataPointView.razor.css +++ b/JournalApp/Components/DataPointView.razor.css @@ -5,6 +5,13 @@ gap: 8px; } +/* Fixed-width tabular readout so 9.5 -> 10.0 doesn't shift the slider. */ +::deep .sleep-hours { + min-width: 4ch; + text-align: center; + font-variant-numeric: tabular-nums; +} + .note-container { display: flex; flex-direction: column; diff --git a/JournalApp/Data/PreferenceService.cs b/JournalApp/Data/PreferenceService.cs index 4fb53b38..f9bf1bef 100644 --- a/JournalApp/Data/PreferenceService.cs +++ b/JournalApp/Data/PreferenceService.cs @@ -15,14 +15,15 @@ public sealed partial class PreferenceService : IPreferences, IDisposable private MudTheme _mudTheme; private uint _mudThemeSeed; - // Mood colors are HCT values generated with material-color-utilities on a "coral reef" hue sweep: turquoise water (210Β°, great) through sand to coral (20Β°, awful). + // Mood colors are HCT values generated with material-color-utilities on a "coral reef" hue sweep: turquoise water (210Β°, great) through coral (20Β°, awful). // The light ramp stays in bright tones (62-86) so the theme's dark text-primary reads on every cell, and the dark ramp uses deep jewel tones (42-50) so the light text-primary does the same. πŸ€” (unset) intentionally has no color. + // 😐 sits at the middle of the ramp as a true neutral grey so it reads as no signal either way. private static readonly Dictionary _lightMoodColors = new() { ["🀩"] = "#2ACADF", ["πŸ˜€"] = "#50DBD0", ["πŸ™‚"] = "#8BE3C0", - ["😐"] = "#EED687", + ["😐"] = "#D9D9D9", ["πŸ˜•"] = "#FFB783", ["😒"] = "#FF9474", ["😭"] = "#F26B6B", @@ -33,7 +34,7 @@ public sealed partial class PreferenceService : IPreferences, IDisposable ["🀩"] = "#007E8C", ["πŸ˜€"] = "#00857E", ["πŸ™‚"] = "#2A8668", - ["😐"] = "#916C24", + ["😐"] = "#737373", ["πŸ˜•"] = "#A65911", ["😒"] = "#A94C2F", ["😭"] = "#A94041", diff --git a/JournalApp/MainPage.xaml b/JournalApp/MainPage.xaml index 0b34a8a3..bcd90c0b 100644 --- a/JournalApp/MainPage.xaml +++ b/JournalApp/MainPage.xaml @@ -1,14 +1,15 @@ -ο»Ώ + + SafeAreaEdges="All" + BackgroundColor="{AppThemeBinding Light=#FFF8F9, Dark=#181215}"> - + - \ No newline at end of file + diff --git a/JournalApp/MainPage.xaml.cs b/JournalApp/MainPage.xaml.cs index 957c618d..605f12fd 100644 --- a/JournalApp/MainPage.xaml.cs +++ b/JournalApp/MainPage.xaml.cs @@ -1,4 +1,6 @@ -ο»Ώnamespace JournalApp; +using Microsoft.AspNetCore.Components.WebView; + +namespace JournalApp; public partial class MainPage : ContentPage { @@ -6,4 +8,14 @@ public MainPage() { InitializeComponent(); } + + // The native WebView paints white before Blazor's first frame, which flashes hard against a dark splash, so let the themed page background show through instead. + private void OnBlazorWebViewInitialized(object sender, BlazorWebViewInitializedEventArgs e) + { +#if ANDROID + e.WebView.SetBackgroundColor(Android.Graphics.Color.Transparent); +#elif WINDOWS + e.WebView.DefaultBackgroundColor = Microsoft.UI.Colors.Transparent; +#endif + } } diff --git a/JournalApp/Pages/SettingsPage.razor b/JournalApp/Pages/SettingsPage.razor index b990fdeb..3e73b9cd 100644 --- a/JournalApp/Pages/SettingsPage.razor +++ b/JournalApp/Pages/SettingsPage.razor @@ -33,7 +33,7 @@ else
Theme - + Light Dark System diff --git a/JournalApp/Platforms/Android/Resources/drawable/appicon_monochrome.xml b/JournalApp/Platforms/Android/Resources/drawable/appicon_monochrome.xml index 5d43191e..dcf88e94 100644 --- a/JournalApp/Platforms/Android/Resources/drawable/appicon_monochrome.xml +++ b/JournalApp/Platforms/Android/Resources/drawable/appicon_monochrome.xml @@ -1,8 +1,8 @@ - + + android:insetLeft="7%" + android:insetTop="7%" + android:insetRight="7%" + android:insetBottom="7%" /> diff --git a/JournalApp/Platforms/Android/Resources/drawable/splash_screen.xml b/JournalApp/Platforms/Android/Resources/drawable/splash_screen.xml index c8c88d3e..4f43886b 100644 --- a/JournalApp/Platforms/Android/Resources/drawable/splash_screen.xml +++ b/JournalApp/Platforms/Android/Resources/drawable/splash_screen.xml @@ -1,6 +1,15 @@ + + + + + + diff --git a/JournalApp/Platforms/Android/Resources/values-night/colors.xml b/JournalApp/Platforms/Android/Resources/values-night/colors.xml index 34688a25..052cb0ce 100644 --- a/JournalApp/Platforms/Android/Resources/values-night/colors.xml +++ b/JournalApp/Platforms/Android/Resources/values-night/colors.xml @@ -1,4 +1,5 @@ - #FFD8EE + + #181215 diff --git a/JournalApp/Platforms/Android/Resources/values-v31/styles.xml b/JournalApp/Platforms/Android/Resources/values-v31/styles.xml index 71087830..89ff0537 100644 --- a/JournalApp/Platforms/Android/Resources/values-v31/styles.xml +++ b/JournalApp/Platforms/Android/Resources/values-v31/styles.xml @@ -1,7 +1,7 @@ + diff --git a/JournalApp/Platforms/Android/Resources/values/colors.xml b/JournalApp/Platforms/Android/Resources/values/colors.xml index 74430eb4..40d24bee 100644 --- a/JournalApp/Platforms/Android/Resources/values/colors.xml +++ b/JournalApp/Platforms/Android/Resources/values/colors.xml @@ -2,5 +2,8 @@ #844C72 #844C72 - #FFD8EE + + #FFF8F9 + + #FFD8EE diff --git a/JournalApp/Resources/AppIcon/appiconfg.svg b/JournalApp/Resources/AppIcon/appiconfg.svg index f172076c..22d1bb9e 100644 --- a/JournalApp/Resources/AppIcon/appiconfg.svg +++ b/JournalApp/Resources/AppIcon/appiconfg.svg @@ -1 +1 @@ - + diff --git a/JournalApp/Resources/AppIcon/appiconfg_debug.svg b/JournalApp/Resources/AppIcon/appiconfg_debug.svg index 557367a9..ddd90517 100644 --- a/JournalApp/Resources/AppIcon/appiconfg_debug.svg +++ b/JournalApp/Resources/AppIcon/appiconfg_debug.svg @@ -1 +1 @@ - + diff --git a/JournalApp/wwwroot/app.css b/JournalApp/wwwroot/app.css index 1fe16a33..65efde0a 100644 --- a/JournalApp/wwwroot/app.css +++ b/JournalApp/wwwroot/app.css @@ -12,11 +12,11 @@ body { display: none; } +/* Background colors come from the theme-matched media queries at the bottom so the native splash, this loading screen, and the app all share one tone. */ #splash-screen { display: flex; align-items: center; justify-content: center; - background-color: Canvas; color: CanvasText; position: fixed; width: 100%; @@ -83,6 +83,22 @@ body { color: var(--mud-palette-text-primary); } +/* On-scroll separation for the sticky header: a soft M3 level-1 shadow fades in once content passes underneath. */ +/* MD3 proper would retone the container instead, but that desyncs the natively colored status bar, so elevation stands in until edge-to-edge lands. */ +@supports (animation-timeline: scroll()) { + .page-header { + animation: header-on-scroll linear both; + animation-timeline: scroll(); + animation-range: 0 56px; + } +} + +@keyframes header-on-scroll { + to { + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 1px 3px 1px rgba(0, 0, 0, 0.15); + } +} + .page-body { width: 100%; max-width: 960px; @@ -448,13 +464,13 @@ label.mud-switch.mud-disabled .mud-switch-span { } @media (prefers-color-scheme: dark) { - .status-bar-safe-area { + .status-bar-safe-area, #splash-screen { background-color: #181215; } } @media (prefers-color-scheme: light) { - .status-bar-safe-area { + .status-bar-safe-area, #splash-screen { background-color: #FFF8F9; } } diff --git a/README.md b/README.md index ca6fd0d6..0fe3dbff 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,9 @@ - πŸ“š **Guided worksheets** for mental health check-ins - 🚨 **Safety Plan** with your coping strategies and support contacts - πŸ’Ύ **Local backups** you can export and import as a single file +- 🎨 **Material You colors** that match your wallpaper on Android 12+ - πŸŒ™ **Light and dark themes** +- πŸ“³ **Haptic feedback** that makes logging quick entries feel tactile ## Privacy @@ -24,7 +26,7 @@ Your data never leaves your device. There is no account, no sign-up, and no clou ## Tech stack - [.NET MAUI](https://learn.microsoft.com/dotnet/maui/) with [Blazor Hybrid](https://learn.microsoft.com/aspnet/core/blazor/hybrid/) for the UI -- [MudBlazor](https://mudblazor.com/) component library +- [MudBlazor](https://mudblazor.com/) component library with custom theming that follows [Material 3 Expressive](https://m3.material.io/), including dynamic color generated from your device palette - [EF Core](https://learn.microsoft.com/ef/core/) with SQLite for local storage - [ApexCharts](https://apexcharts.github.io/Blazor-ApexCharts/) for trends From 183f2bfcf6352b614abdfd9a7acc8725fb58fba1 Mon Sep 17 00:00:00 2001 From: Daniel Chalmers Date: Fri, 17 Jul 2026 15:44:00 -0500 Subject: [PATCH 2/3] update readme --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0fe3dbff..d10f33d8 100644 --- a/README.md +++ b/README.md @@ -17,18 +17,16 @@ - πŸ’Ύ **Local backups** you can export and import as a single file - 🎨 **Material You colors** that match your wallpaper on Android 12+ - πŸŒ™ **Light and dark themes** -- πŸ“³ **Haptic feedback** that makes logging quick entries feel tactile ## Privacy Your data never leaves your device. There is no account, no sign-up, and no cloud. See the [privacy policy](PRIVACY_POLICY.md) for details. -## Tech stack +## Tech - [.NET MAUI](https://learn.microsoft.com/dotnet/maui/) with [Blazor Hybrid](https://learn.microsoft.com/aspnet/core/blazor/hybrid/) for the UI -- [MudBlazor](https://mudblazor.com/) component library with custom theming that follows [Material 3 Expressive](https://m3.material.io/), including dynamic color generated from your device palette +- [MudBlazor](https://mudblazor.com/) components with custom [Material 3 Expressive](https://m3.material.io/) theming - [EF Core](https://learn.microsoft.com/ef/core/) with SQLite for local storage -- [ApexCharts](https://apexcharts.github.io/Blazor-ApexCharts/) for trends ## Contributing From 3de253b87c4ded0cfd0cee587448bd3e342ffe69 Mon Sep 17 00:00:00 2001 From: Daniel Chalmers Date: Fri, 17 Jul 2026 15:57:15 -0500 Subject: [PATCH 3/3] Fix Trends label collisions and finish M3E controls - Cut Trends x-axis ticks to 6 so date labels never overlap on phones - Hide the numeric field's spin buttons and underline so it reads like the other rows - Restyle toggle groups as M3E connected button groups: tonal segments, 2px gaps, selected pill morph - Add the M3E slider handle gap and end stop indicator --- JournalApp/Components/DataPointView.razor | 12 +++---- JournalApp/Pages/SettingsPage.razor | 2 +- .../Pages/Trends/TrendCategoryView.razor | 3 +- JournalApp/wwwroot/app.css | 33 +++++++++++++++---- 4 files changed, 36 insertions(+), 14 deletions(-) diff --git a/JournalApp/Components/DataPointView.razor b/JournalApp/Components/DataPointView.razor index 63e455b8..07681250 100644 --- a/JournalApp/Components/DataPointView.razor +++ b/JournalApp/Components/DataPointView.razor @@ -25,7 +25,7 @@ else if (Point.Type == PointType.Scale) } else if (Point.Type == PointType.LowToHigh) { - + None Low Medium @@ -34,7 +34,7 @@ else if (Point.Type == PointType.LowToHigh) } else if (Point.Type == PointType.MildToSevere) { - + None Mild Moderate @@ -43,18 +43,18 @@ else if (Point.Type == PointType.MildToSevere) } else if (Point.Type == PointType.Bool) { - + No Yes } else if (Point.Type == PointType.Number) { - + } else if (Point.Type == PointType.Text) { - + } else if (Point.Type == PointType.Note) { @@ -75,7 +75,7 @@ else if (Point.Type == PointType.Note) else if (Point.Type == PointType.Medication) {
- + No Yes diff --git a/JournalApp/Pages/SettingsPage.razor b/JournalApp/Pages/SettingsPage.razor index 3e73b9cd..037f6b0c 100644 --- a/JournalApp/Pages/SettingsPage.razor +++ b/JournalApp/Pages/SettingsPage.razor @@ -33,7 +33,7 @@ else
Theme - + Light Dark System diff --git a/JournalApp/Pages/Trends/TrendCategoryView.razor b/JournalApp/Pages/Trends/TrendCategoryView.razor index da2903ae..b6a78fb8 100644 --- a/JournalApp/Pages/Trends/TrendCategoryView.razor +++ b/JournalApp/Pages/Trends/TrendCategoryView.razor @@ -146,7 +146,8 @@ else }, Xaxis = new XAxis { - TickAmount = 15, + // Few enough ticks that M/d labels never collide on a phone-width chart. + TickAmount = 6, Labels = new XAxisLabels { HideOverlappingLabels = true, diff --git a/JournalApp/wwwroot/app.css b/JournalApp/wwwroot/app.css index 65efde0a..97ca5161 100644 --- a/JournalApp/wwwroot/app.css +++ b/JournalApp/wwwroot/app.css @@ -142,23 +142,38 @@ body { gap: 8px; } -/* M3 segmented buttons: outlined container, neutral unselected text, selected segment fills secondaryContainer. */ +/* M3E connected button group: tonal segments separated by 2px gaps instead of an outlined pill with dividers. */ +/* The row of segments keeps fully rounded outer corners, and the selected segment morphs to a full pill with a secondaryContainer fill. */ .mud-toggle-group { flex-grow: 1; - border-radius: 999px; - border-color: var(--mud-palette-lines-inputs) !important; + gap: 2px; + border: none !important; + background-color: transparent; } .mud-toggle-group .mud-toggle-item { color: var(--mud-palette-text-primary); - background-color: transparent; - border-color: var(--mud-palette-lines-inputs) !important; + background-color: var(--mud-palette-gray-lighter); + border: none !important; + border-radius: 8px; min-height: 32px; + transition: border-radius 0.15s ease-out, background-color 0.15s ease-out; +} + +.mud-toggle-group .mud-toggle-item:first-child { + border-top-left-radius: 999px; + border-bottom-left-radius: 999px; +} + +.mud-toggle-group .mud-toggle-item:last-child { + border-top-right-radius: 999px; + border-bottom-right-radius: 999px; } .mud-toggle-group .mud-toggle-item.mud-toggle-item-selected { background-color: var(--mud-palette-secondary-lighten); color: var(--mud-palette-secondary-darken); + border-radius: 999px; } .mud-button { @@ -191,8 +206,13 @@ body { border-radius: 999px; } +/* The inactive track carries the M3E stop indicator dot near its trailing end. */ .mud-slider.mud-slider-primary .mud-slider-input::-webkit-slider-runnable-track { background-color: var(--mud-palette-secondary-lighten); + background-image: radial-gradient(circle, var(--mud-palette-primary) 0 2px, transparent 2.5px); + background-repeat: no-repeat; + background-position: right 5px center; + background-size: 5px 5px; } .mud-slider .mud-slider-filled, @@ -208,7 +228,8 @@ body { transform: none; border-radius: 2px; margin-top: -6px; - box-shadow: none; + /* M3E carves a gap between the handle and the track; surface-colored blocks either side of the bar fake it over the row container. */ + box-shadow: 6px 0 0 var(--mud-palette-surface), -6px 0 0 var(--mud-palette-surface); } /* M3 switch: 52x32 pill track with an outline, solid colors instead of translucent overlays, and a thumb that grows when checked. */