feat: mobile-first nav redesign (Phase 10e-5) #25
No reviewers
Labels
No labels
domain:backend
domain:devops
domain:frontend
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
forgejo_admin/westside-landing!25
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "24-mobile-first-nav-redesign-phase-10e-5"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Rewrites
AuthStatus.sveltewith a responsive mobile-first navigation. On phones (<768px), a fixed hamburger button opens a slide-out drawer with user info, role badge, full-width tappable nav links, and a sign out button. On desktop (>=768px), a clean horizontal nav bar renders instead. Also removes the duplicate "Manage Users" link from the admin page since it now lives in the global nav.Changes
src/lib/components/AuthStatus.svelte— Full rewrite. Two separate<nav>elements (desktop + mobile) toggled via CSS media queries. Mobile drawer with backdrop overlay, animated hamburger-to-X icon, smooth 200ms slide transition. Nav items computed as a$derivedarray based on user roles. Uses CSS custom properties from design system exclusively. SSR-safe.src/routes/admin/+page.svelte— Removed duplicate<div class="nav-links">section and its associated CSS styles since "Manage Users" is now accessible from the global nav for admin users.Test Plan
/adminpage no longer has duplicate "Manage Users" linknpm run buildpassesReview Checklist
Related
plan-pal-e-platform— Phase 10e-5 mobile nav redesign- Remove class:visible={drawerOpen} from backdrop button -- the element is already conditionally rendered with {#if drawerOpen}, and .visible class was never defined in styles. - Change mobile nav aria-label from "Main navigation" to "Mobile navigation" to avoid duplicate landmark labels (a11y). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>Self-Review Findings
Reviewed the diff (350 additions, 90 deletions across 2 files). Found 2 issues, both fixed in commit
ec9fd5a.Fixed
Dead
class:visibledirective — The backdrop<button>hadclass:visible={drawerOpen}but the element is already conditionally rendered inside{#if drawerOpen}, and no.visibleCSS class was ever defined. Removed the dead directive.Duplicate
aria-labelon nav landmarks — Both<nav>elements hadaria-label="Main navigation", which creates duplicate landmark labels for screen readers. Changed mobile nav toaria-label="Mobile navigation".Verified
app.csswindowreferences -- SSR-safetransform(GPU-composited, no layout thrashing)npm run buildpasses after fixesVERDICT: APPROVE
PR #25 Review
DOMAIN REVIEW
Tech stack: SvelteKit 5 (runes mode), CSS custom properties, Keycloak OIDC auth.
Accessibility (Good)
aria-labelon both<nav>elements ("Main navigation", "Mobile navigation") -- solid.aria-label("Close menu" / "Open menu") andaria-expandedbinding.<button>element (not a<div>) for keyboard accessibility, witharia-label="Close menu".role="dialog"witharia-label="Navigation menu".aria-hidden="true".Accessibility (Concerns)
role="dialog"lacks focus trapping. When the drawer opens, keyboard focus stays on the hamburger button. A keyboard user can Tab past the drawer into the page content behind the backdrop. This is a meaningful a11y gap for a dialog pattern, though not a blocker for a nav drawer in a small internal app.Escapekey handler to close the drawer. Standard expectation for dialog/drawer patterns.tabindex="-1"on the backdrop means it is removed from tab order, which is correct, but the drawer links themselves are still tabbable when the drawer is closed (it is just visually hidden viatransform). Screen readers and keyboard users can reach them in the closed state.Responsive Design (Good)
.desktop-navisdisplay: noneby default, shown at>=768px..mobile-navisdisplay: blockby default, hidden at>=768px. No overlap.max-width: 85vw-- prevents overflow on small screens (375px).Component Architecture (Good)
navItemscomputed as a$derivedarray based on roles -- single source of truth for both desktop and mobile renders. Eliminates the DRY violation that existed before (nav links duplicated in template markup).userNameandprimaryRoleextracted as derived values -- clean.windowordocumentreferences, noonMountside effects.CSS Quality (Good)
app.css). No hardcoded color values in the diff.@media (max-width: 480px)responsive block -- no dead CSS left behind.Admin Page Cleanup (Good)
/admin. This link now lives in the global nav for admin users. Clean scope reduction.BLOCKERS
1. Hardcoded Keycloak URL in client-side component
/home/ldraney/westside-app/src/lib/components/AuthStatus.svelteline 42:This is a hardcoded URL in a client-rendered component. However, this is a pre-existing pattern -- the same URL was hardcoded in the old code (visible in the diff's removed lines). The server-side module
keycloak-admin.jsalso hardcodes it. This PR does not introduce the problem, it preserves it.Verdict on this item: NOT a blocker for this PR. The hardcoded URL is pre-existing technical debt. Should be tracked as a separate issue (extract to environment variable or config).
No new blockers found. This PR does not introduce new functionality that requires test coverage -- it is a pure UI refactor of an existing component. The nav items are the same, the auth flow is unchanged. The repo has zero test files (no test infrastructure exists), so demanding tests for a CSS/layout refactor would be unreasonable. No unvalidated user input, no secrets, no DRY violations in auth paths.
NITS
Focus trap missing on drawer dialog. The
role="dialog"drawer does not trap focus. When opened, Tab can reach elements behind the backdrop. For a small internal app this is acceptable, but if the app grows in user base, adding a focus trap (e.g.,svelte-focus-trapor a manual implementation) and anEscapekey handler would bring it to WCAG 2.1 AA compliance. Consider filing a follow-up issue.Drawer links tabbable when closed. The drawer is hidden via
transform: translateX(100%)but the links remain in the tab order. Addinginertattribute to the drawer when closed (or togglingvisibility: hiddenwith a transition delay) would prevent keyboard users from accidentally tabbing into the off-screen drawer.Pre-existing: Keycloak URL should be an env var. The
Manage Accounthref and all URLs inkeycloak-admin.jsshould come from a shared config or environment variable. Not introduced by this PR, but worth a follow-up issue.plan-pal-e-platformreference in Related section. The PR body saysplan-pal-e-platform -- Phase 10e-5. The user specified the plan isplan-2026-03-08-tryout-prep. The plan reference in the PR body appears to be pointing to the wrong plan. Minor documentation nit.SOP COMPLIANCE
24-mobile-first-nav-redesign-phase-10e-5(references issue #24)plan-pal-e-platformbut user specifiedplan-2026-03-08-tryout-prepCloses #24present in PR bodyPROCESS OBSERVATIONS
.test.jsor.spec.jsfiles). The Test Plan in the PR body is a manual checklist, which is appropriate for a frontend component refactor in a repo without automated test tooling. A follow-up to add Playwright or Vitest component tests would reduce change failure risk for future PRs.navItemsarray and removes the admin page duplicate. Net improvement.VERDICT: APPROVED
Clean mobile-first refactor with good accessibility foundations, proper use of design system tokens, and a DRY improvement over the previous implementation. No blockers found. The nits (focus trap, drawer tabbability, plan slug mismatch) are non-blocking and can be addressed in follow-up work.