From 26d977cdc14f1636581bdbb28595e5b536cf155e Mon Sep 17 00:00:00 2001 From: Mao Nakamoto <41178744+maonakamoto@users.noreply.github.com> Date: Mon, 31 Aug 2026 09:27:12 +0200 Subject: [PATCH] fix(nav): the admin overflow sheet was the one surface that stayed silent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sidebar and the bottom bar both set aria-current. The "More" sheet did not — so the destinations that happened to land behind the overflow were precisely the ones a screen-reader user could not locate themselves in. `isActive` and `pathname` were already in scope; the call was simply missing. Two more defects on the same surface, both of which the rest of the app already gets right: - `.moreSheetItem` came out near 41px tall — 0.65rem of padding around 0.85rem text at this root size. `.bottomNavItem` states `min-height: 44px` explicitly; the overflow sheet is the same kind of touch surface, so it now says the minimum instead of arriving near it by arithmetic. - Hover and focus were one rule ending in `outline: none`, so keyboard focus was indicated by a 7%-white wash and nothing else: the weakest possible signal, on the surface where a keyboard user most needs to know where they are. Focus now gets a real ring; hover keeps the wash. Found by a fleet-wide navigation audit across 20 repos, which found the same one-straggler shape in aoz-housing, fleetcrown and evig — four teams applying a rule correctly on every surface but one. Typecheck clean; 1306 tests pass. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01XeELB8b3N4JrT2asYL9WvE --- app/(admin)/admin.module.css | 17 +++++++++++++++-- components/admin/AdminNav.tsx | 6 ++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/app/(admin)/admin.module.css b/app/(admin)/admin.module.css index 65b860c..2cc5755 100644 --- a/app/(admin)/admin.module.css +++ b/app/(admin)/admin.module.css @@ -2181,6 +2181,11 @@ display: flex; align-items: center; gap: 0.65rem; + /* 0.65rem of padding around 0.85rem text lands near 41px at this root + size — just under the floor .bottomNavItem already states explicitly. + The overflow sheet is a touch surface like any other; say the minimum + rather than arriving near it by arithmetic. */ + min-height: 44px; padding: 0.65rem 0.85rem; border-radius: var(--radius-lg); color: var(--white-90); @@ -2188,10 +2193,18 @@ font-size: 0.85rem; } - .moreSheetItem:hover, + .moreSheetItem:hover { + background: var(--white-7); + } + + /* Hover and focus were one rule, so keyboard focus was indicated by a 7% + white wash and nothing else — the weakest possible signal, on the surface + where a keyboard user most needs to know where they are. Focus gets a real + ring; hover keeps the wash. */ .moreSheetItem:focus-visible { background: var(--white-7); - outline: none; + outline: 2px solid var(--white-90); + outline-offset: -2px; } /* Tighter header padding */ diff --git a/components/admin/AdminNav.tsx b/components/admin/AdminNav.tsx index eaa3dd4..68700d0 100644 --- a/components/admin/AdminNav.tsx +++ b/components/admin/AdminNav.tsx @@ -353,11 +353,17 @@ export function AdminBottomNav(props: AdminBadgeProps) { const Icon = ROUTE_ICONS[href]; const count = badgeCount(href, badges); const urgent = ROUTE_BADGE_KEYS[href] === "patients"; + // The sidebar and the bottom bar both announce the current page. + // The overflow sheet did not — so the destinations that happened + // to land behind "More" were the ones a screen-reader user could + // not locate themselves in. + const active = isActive(pathname, href); return ( setMoreOpen(false)} >