Two new SSR tests in `app-shell.test.tsx`:
- Desktop sidebar's brand header is now a link, not a static <div>.
Asserts the <aside> contains an <a href="/" aria-label="Go to
dashboard"> at the top. The slice is scoped to the <aside> via a
new `extractSidebar` helper so it can't accidentally match the
mobile-header brand link.
- Mobile and desktop brand links carry distinct aria-labels
("Go home" vs "Go to dashboard"). On a wide window where the
desktop sidebar is visible alongside the (sm:hidden) mobile
header — which technically can't happen at any one breakpoint
but is a useful invariant for screen readers in split-screen /
zoom contexts — the two announcements stay distinguishable.
Total: 212 web tests passing (was 210).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
12 new SSR tests in app-shell.test.tsx:
Mobile header
- Fixed top header is rendered with `sm:hidden` so it disappears on
the desktop breakpoint.
- Brand mark on the left links home and carries `aria-label="Go home"`.
- Page title in the centre is derived from usePathname:
* "/" → "Dashboard"
* "/accounts/123" → "Accounts" (sub-route falls back to parent label)
* unknown route → generic "WhatsApp Bot"
- Menu button on the right is labelled `aria-label="Open menu"`.
Menu drawer (Sheet primitives mocked transparent so SSR shows content)
- Renders one nav link per NAV_ITEM, in declared order.
- The active route's link gets `aria-current="page"`; others don't.
- Dashboard ("/") matches by exact equality, not by `startsWith`, so
every page doesn't get marked Dashboard.
- The drawer does NOT include the theme toggle — it lives only in
the desktop sidebar footer per the recent product call.
- Drawer header carries the brand wording and the SR-only nav-menu
description.
Desktop sidebar
- Renders with `hidden sm:flex` (mobile-hidden, desktop-visible).
- All NAV_ITEMS appear.
- Theme toggle is present in the sidebar footer.
Plus the small follow-up the user pointed at:
UI: status tabs span the full row
- The shadcn `<TabsList>` defaults to `inline-flex w-fit`, which
packed Active/Ended/Paused into a tight cluster on the left of
the reminders + activity pages. Added `w-full` to both
`<TabsList>` invocations so the tabs distribute evenly across
the available row width (`flex-1` on each `<TabsTrigger>` already
handles even widths once the parent stretches).
Total: 206 web tests passing (was 194; +12 from app-shell.test.tsx).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>