diff --git a/docs/tables_frontend_overhaul_implementation_plan.md b/docs/tables_frontend_overhaul_implementation_plan.md index 78f6752..ae93d79 100644 --- a/docs/tables_frontend_overhaul_implementation_plan.md +++ b/docs/tables_frontend_overhaul_implementation_plan.md @@ -46,6 +46,7 @@ It is intentionally implementation-focused: | 2026-03-21 | P1.6 | Completed | Added explicit shell slots for nav, omnibox, shortcuts, and utilities; switched shell navigation to `Play`, `Tables`, `Curation`, and `Tools`; and wired the first live theme control into the shell. | | 2026-03-21 | P1.7 | Completed | Added a shell-level skip link and tightened the top-level header, navigation, and main landmarks around the new shell structure. | | 2026-03-21 | P1.8 | Completed | Introduced a cooler tooling emphasis for `Tools`, diagnostics, and API surfaces, and styled the `Tools` destination as distinct without splitting the shell. | +| 2026-03-21 | Post-P1 fix 1 | Completed | Closed the 768px-1023px navigation gap by adding a shell hamburger menu and drawer so primary navigation never disappears at tablet widths. | ### Lessons Learned @@ -61,6 +62,7 @@ It is intentionally implementation-focused: - Once a Razor component exposes multiple named `RenderFragment` parameters, the page body must be passed explicitly through ``. That pattern is now the baseline for shell composition here. - Accessibility work is cheaper when the shell owns the landmarks. Adding skip links and nav/main structure at the shell layer avoids repeating that work page by page. - Tooling can feel distinct through cooler surfaces and labeling alone. A separate app shell is unnecessary and would undermine the shared-product goal. +- Responsive shell design needs an explicit tablet state, not just desktop and phone states. The original breakpoints left a navigation dead zone between the top nav and bottom nav layouts. ## Target Outcomes diff --git a/src/RolemasterDb.App/Components/Shell/AppShell.razor b/src/RolemasterDb.App/Components/Shell/AppShell.razor index 78dfe76..cc01ecc 100644 --- a/src/RolemasterDb.App/Components/Shell/AppShell.razor +++ b/src/RolemasterDb.App/Components/Shell/AppShell.razor @@ -1,3 +1,6 @@ +@implements IDisposable +@inject NavigationManager NavigationManager +
@@ -39,6 +42,17 @@
+ @UtilityContent
@@ -57,6 +71,26 @@ + @if (isNavMenuOpen) + { + + + + } + @@ -80,4 +114,32 @@ [Parameter] public RenderFragment? UtilityContent { get; set; } + + private bool isNavMenuOpen; + + protected override void OnInitialized() + { + NavigationManager.LocationChanged += HandleLocationChanged; + } + + private void ToggleNavMenu() + { + isNavMenuOpen = !isNavMenuOpen; + } + + private void CloseNavMenu() + { + isNavMenuOpen = false; + } + + private void HandleLocationChanged(object? sender, LocationChangedEventArgs args) + { + isNavMenuOpen = false; + _ = InvokeAsync(StateHasChanged); + } + + public void Dispose() + { + NavigationManager.LocationChanged -= HandleLocationChanged; + } } diff --git a/src/RolemasterDb.App/Components/Shell/AppShell.razor.css b/src/RolemasterDb.App/Components/Shell/AppShell.razor.css index c63cff0..cb46234 100644 --- a/src/RolemasterDb.App/Components/Shell/AppShell.razor.css +++ b/src/RolemasterDb.App/Components/Shell/AppShell.razor.css @@ -103,6 +103,28 @@ min-height: 2.75rem; } +.app-shell-menu-toggle { + display: none; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 0.24rem; + width: 2.75rem; + height: 2.75rem; + border: 1px solid var(--border-default); + border-radius: 16px; + background: color-mix(in srgb, var(--surface-2) 84%, transparent); + color: var(--text-primary); + padding: 0; +} + +.app-shell-menu-toggle-bar { + width: 1rem; + height: 2px; + border-radius: 999px; + background: currentColor; +} + .app-shell-shortcuts { margin-top: 0.75rem; } @@ -128,6 +150,55 @@ margin-top: auto; } +.app-shell-drawer-backdrop { + position: fixed; + inset: 0; + z-index: 45; + border: none; + background: var(--bg-overlay); + padding: 0; +} + +.app-shell-drawer { + position: fixed; + top: 1rem; + right: 1rem; + z-index: 50; + width: min(24rem, calc(100vw - 2rem)); + padding: 1rem; + border: 1px solid var(--border-default); + border-radius: 24px; + background: color-mix(in srgb, var(--bg-elevated) 94%, transparent); + box-shadow: var(--shadow-2); + backdrop-filter: blur(18px); +} + +.app-shell-drawer .shell-primary-nav { + flex-direction: column; + align-items: stretch; +} + +.app-shell-drawer .shell-primary-nav-link { + justify-content: flex-start; +} + +.app-shell-drawer-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.75rem; + margin-bottom: 0.75rem; +} + +.app-shell-drawer-close { + border: 1px solid var(--border-default); + border-radius: 999px; + background: color-mix(in srgb, var(--surface-2) 84%, transparent); + color: var(--text-primary); + min-height: 2.5rem; + padding: 0.45rem 0.85rem; +} + @media (max-width: 767.98px) { .app-shell-header { padding: 0.65rem 0.75rem 0; @@ -170,4 +241,15 @@ .app-shell-header-nav { display: none; } + + .app-shell-menu-toggle { + display: inline-flex; + } +} + +@media (min-width: 1024px) { + .app-shell-drawer, + .app-shell-drawer-backdrop { + display: none; + } }