feat(web): responsive + a11y sweep across remaining routes #561

Merged
code-lead merged 2 commits from boss/558 into main 2026-04-30 00:13:21 +00:00
Collaborator

Summary

  • Brings every web route up to the WCAG 2.1 AA + responsive baseline that the agents page (#555) established.
  • Adds the <Tabs> foundation primitive (roving tabindex, arrow-key nav) and documents the primitives + a11y baseline in apps/web/CLAUDE.md so the next sweep doesn't have to relitigate conventions.
  • Sweeps /flows, /monitor, /settings, /login — Unicode glyphs → lucide icons, raw <button><Button> (incl. tone="error"), inputs labelled, hand-rolled drawers migrated to <Drawer>, mobile card stacks where a desktop table would overflow.
  • Cross-route: hamburger nav drawer below md: (every NAV_ITEM reachable on a phone), Esc dismisses the newest toast, save-status wrapped in role="status" aria-live="polite".

Closes #558

Test plan

  • bun x turbo run typecheck (4 packages)
  • bun x @biomejs/biome@^2 check .
  • bun x turbo run test — 597 web tests + 2601 server tests pass
  • Manual: hamburger drawer at 375 px viewport reaches Specs / Flows / Settings / Stats
  • Manual: tab + esc keyboard flow through <Drawer> instances (TaskDrawer, hamburger nav)
  • Manual: VoiceOver / NVDA reads role="status" save announcement on /settings
## Summary - Brings every web route up to the WCAG 2.1 AA + responsive baseline that the agents page (#555) established. - Adds the `<Tabs>` foundation primitive (roving tabindex, arrow-key nav) and documents the primitives + a11y baseline in `apps/web/CLAUDE.md` so the next sweep doesn't have to relitigate conventions. - Sweeps `/flows`, `/monitor`, `/settings`, `/login` — Unicode glyphs → lucide icons, raw `<button>` → `<Button>` (incl. `tone="error"`), inputs labelled, hand-rolled drawers migrated to `<Drawer>`, mobile card stacks where a desktop table would overflow. - Cross-route: hamburger nav drawer below `md:` (every NAV_ITEM reachable on a phone), Esc dismisses the newest toast, save-status wrapped in `role="status" aria-live="polite"`. Closes #558 ## Test plan - [x] `bun x turbo run typecheck` (4 packages) - [x] `bun x @biomejs/biome@^2 check .` - [x] `bun x turbo run test` — 597 web tests + 2601 server tests pass - [ ] Manual: hamburger drawer at 375 px viewport reaches Specs / Flows / Settings / Stats - [ ] Manual: tab + esc keyboard flow through `<Drawer>` instances (TaskDrawer, hamburger nav) - [ ] Manual: VoiceOver / NVDA reads `role="status"` save announcement on /settings
feat(web): responsive + a11y sweep across remaining routes (#558)
All checks were successful
qa / dockerfile (pull_request) Successful in 7s
qa / qa (pull_request) Successful in 2m3s
4475a81664
Brings every web route up to the WCAG 2.1 AA + responsive baseline that
the agents page (#555) established.

Foundation
- Add `<Tabs>` primitive (roving tabindex, arrow-key nav, manual activation).
- Document foundation primitives + a11y baseline in apps/web/CLAUDE.md.

/flows
- Replace Unicode glyphs (✕ ) with lucide icons; raw <button> → <Button>.
- Filter bar wrapped in <search>; every <select>/<input> labelled.
- Mobile card list (FlowCard) below `md:` via useMediaQuery.

/monitor
- TaskDrawer migrated from hand-rolled <aside> to <Drawer> primitive.
- "Recent tasks" rendered via <Button leadingIcon={ListChecks}>.
- Drawer-close test waits for the deferred onClose (200 ms slide-out).

/settings
- ✓ / ℹ glyphs → lucide Check / Info; rotate button → <Button loading>.
- Save status wrapped in role="status" aria-live="polite".
- Repository rows stack on mobile.

/login
- Provider buttons get aria-label + LogIn icon; nav has aria-describedby.
- Padding tuned for 320 px viewports.

Cross-route
- AppShell: hamburger trigger + nav <Drawer> below `md:` (all NAV_ITEMS reachable).
- Toasts: Esc dismisses newest; each toast has descriptive aria-label.
- RunsDrawer: ▼ ▲ → ChevronDown / ChevronUp; toggle wires aria-controls.

Closes #558

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
reviewer requested changes 2026-04-30 00:03:22 +00:00
Dismissed
reviewer left a comment

CI green. One real a11y bug in the new <Tabs> primitive blocks approval; two nits below.

  • a11yapps/web/src/components/tabs.tsx TabsPanel (line ~388): the <div role="tabpanel"> has focus-visible:ring-2 styles but no tabIndex, so the panel is not in the keyboard tab-order and those styles are unreachable. Per WAI-ARIA APG manual-activation pattern, the panel element should carry tabIndex={0} when it cannot guarantee focusable descendants (which a generic primitive cannot). Fix: add tabIndex={0} to the div.

  • docapps/web/CLAUDE.md diff lines 51–53: two consecutive --- separators were inserted; remove one.

  • a11yapps/web/src/features/flows/RunsDrawer.tsx DrawerHeader: the toggle is still a raw <button type="button"> — not <Button> — contrary to the convention this PR codifies. Low-risk since it has hand-crafted focus-visible styles, but worth tidying for consistency.

CI green. One real a11y bug in the new `<Tabs>` primitive blocks approval; two nits below. - **a11y** — `apps/web/src/components/tabs.tsx` `TabsPanel` (line ~388): the `<div role="tabpanel">` has `focus-visible:ring-2` styles but no `tabIndex`, so the panel is not in the keyboard tab-order and those styles are unreachable. Per WAI-ARIA APG manual-activation pattern, the panel element should carry `tabIndex={0}` when it cannot guarantee focusable descendants (which a generic primitive cannot). Fix: add `tabIndex={0}` to the div. - **doc** — `apps/web/CLAUDE.md` diff lines 51–53: two consecutive `---` separators were inserted; remove one. - **a11y** — `apps/web/src/features/flows/RunsDrawer.tsx` `DrawerHeader`: the toggle is still a raw `<button type="button">` — not `<Button>` — contrary to the convention this PR codifies. Low-risk since it has hand-crafted focus-visible styles, but worth tidying for consistency.
reviewer requested changes 2026-04-30 00:03:28 +00:00
Dismissed
reviewer left a comment

CI green. One real a11y bug in the new <Tabs> primitive blocks approval; two nits below.

  • a11yapps/web/src/components/tabs.tsx TabsPanel (~line 388): the <div role="tabpanel"> has focus-visible:ring-2 styles but no tabIndex, so the panel is not in the keyboard tab-order and those styles are unreachable. Per WAI-ARIA APG manual-activation pattern, the panel element should carry tabIndex={0} when it cannot guarantee focusable descendants (which a generic primitive cannot). Fix: add tabIndex={0} to the div.

  • docapps/web/CLAUDE.md diff lines 51-53: two consecutive --- separators were inserted; remove one.

  • a11yapps/web/src/features/flows/RunsDrawer.tsx DrawerHeader: toggle is still a raw <button type="button">, contrary to the convention this PR codifies. Has hand-crafted focus-visible styles so not broken, but worth tidying.

CI green. One real a11y bug in the new `<Tabs>` primitive blocks approval; two nits below. - **a11y** — `apps/web/src/components/tabs.tsx` `TabsPanel` (~line 388): the `<div role="tabpanel">` has `focus-visible:ring-2` styles but no `tabIndex`, so the panel is not in the keyboard tab-order and those styles are unreachable. Per WAI-ARIA APG manual-activation pattern, the panel element should carry `tabIndex={0}` when it cannot guarantee focusable descendants (which a generic primitive cannot). Fix: add `tabIndex={0}` to the div. - **doc** — `apps/web/CLAUDE.md` diff lines 51-53: two consecutive `---` separators were inserted; remove one. - **a11y** — `apps/web/src/features/flows/RunsDrawer.tsx` `DrawerHeader`: toggle is still a raw `<button type="button">`, contrary to the convention this PR codifies. Has hand-crafted focus-visible styles so not broken, but worth tidying.
fix(web): tabpanel focus-order, drawer toggle Button, doc rule
All checks were successful
qa / dockerfile (pull_request) Successful in 5s
qa / qa (pull_request) Successful in 1m43s
7dc1045f90
Address PR #561 review:
- tabs.tsx: TabsPanel now carries tabIndex={0} so the focus-visible
  ring is reachable when descendants aren't guaranteed focusable
  (WAI-ARIA APG manual-activation pattern).
- RunsDrawer.tsx: DrawerHeader migrates from raw <button> to <Button>
  for consistency with the convention codified in this PR.
- apps/web/CLAUDE.md: drop the duplicate `---` separator under the
  reduced-motion section.
Author
Collaborator

Addressed in 7dc1045:

  • a11y / tabs.tsxTabsPanel now carries tabIndex={0} so the focus-visible ring is reachable per WAI-ARIA APG. Suppressed noNoninteractiveTabindex with a justification comment.
  • a11y / RunsDrawer.tsxDrawerHeader migrates from raw <button> to <Button variant="ghost" size="sm">.
  • doc / CLAUDE.md — removed the duplicate --- under the reduced-motion section.

bun x turbo run typecheck, bun x @biomejs/biome@^2 check ., and the 597 web tests pass locally.

Addressed in `7dc1045`: - **a11y / tabs.tsx** — `TabsPanel` now carries `tabIndex={0}` so the focus-visible ring is reachable per WAI-ARIA APG. Suppressed `noNoninteractiveTabindex` with a justification comment. - **a11y / RunsDrawer.tsx** — `DrawerHeader` migrates from raw `<button>` to `<Button variant="ghost" size="sm">`. - **doc / CLAUDE.md** — removed the duplicate `---` under the reduced-motion section. `bun x turbo run typecheck`, `bun x @biomejs/biome@^2 check .`, and the 597 web tests pass locally.
reviewer approved these changes 2026-04-30 00:12:22 +00:00
reviewer left a comment

All three round-1 findings resolved: tabIndex={0} on TabsPanel, duplicate --- removed from CLAUDE.md, RunsDrawer toggle now uses <Button>. CI green.

All three round-1 findings resolved: `tabIndex={0}` on `TabsPanel`, duplicate `---` removed from CLAUDE.md, `RunsDrawer` toggle now uses `<Button>`. CI green.
code-lead deleted branch boss/558 2026-04-30 00:13:22 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
charles/claude-hooks!561
No description provided.