Skip to content

Accessibility: complete the manual browser + screen reader a11y pass #768

@daler91

Description

@daler91

Context

The UX architecture review in #765 flagged a full WCAG AA audit + NVDA/VoiceOver pass as outstanding work. PR B in the deferred-items sequence landed the automatable pieces:

  • jest-axe matcher wired up in vitest.setup.ts
  • Axe tests on NotFound, SuggestionCard, WorkoutHeader, CoachReviewingIndicator, TimelineWorkoutCard
  • Keyboard tab-order + Enter/Space activation tests on TimelineWorkoutCard
  • Static regression test asserting the prefers-reduced-motion override remains in index.css

Everything in that PR runs in CI via pnpm test. The items below require a real browser + human input and can't be automated from the Claude Code sandbox.

Outstanding manual work

1. WCAG AA color-contrast audit in a real browser

Run axe DevTools (or Accessibility Insights for Web) against each of the main pages, in both light and dark themes:

  • / — Timeline (empty state + with data)
  • /log — Log Workout (both exercise mode and text mode)
  • /analytics — Overview / Progression / Records / Breakdown tabs
  • /settings — all cards
  • / — Coach panel open (desktop + mobile bottom sheet)
  • Onboarding wizard (all 5 steps)
  • Landing page (Hero, Features, FAQ, CTA sections)

What to report: every contrast issue with ratio < 4.5:1 for normal text or < 3:1 for large text / non-text UI components.

2. Screen reader pass (NVDA on Windows, VoiceOver on macOS)

  • Timeline virtualized list@tanstack/react-virtual drops off-viewport rows from the DOM, which can strand SR users. Verify the total item count is announced and that scrolling reveals rows correctly.
  • Onboarding wizard — verify the "Step N of M" counter and the sr-only progress element are both announced at each step transition.
  • Coach panel — verify streaming messages announce via the role="log" aria-live="polite" region on CoachPanelChatArea.
  • Coach reviewing banner — verify role="status" aria-live="polite" on the banner announces when isAutoCoaching flips true.
  • Offline indicator — verify the destructive banner and the "Back online — N synced" confirmation both announce.
  • Toast notifications — verify toast variants (including action buttons like Undo / Try again) are announced and reachable.

3. Mobile real-device testing

  • Touch target sizes — WCAG 2.5.5 recommends ≥ 44×44 CSS px for all interactive elements. Spot-check the Coach mobile bottom sheet close affordance, the sidebar collapse trigger, Timeline card mark-complete circles, and the onboarding wizard buttons.
  • Mobile Coach bottom sheet — verify focus is trapped within the sheet while open and returned to the FAB on close.
  • Reduced motion on iOS / Android — confirm the global override in index.css actually takes effect with OS-level motion reduction enabled.

4. Focus management spot checks

  • After the onboarding wizard closes, focus should move to the first actionable element on Timeline (or remain somewhere sensible, not the body).
  • Opening and closing any Radix dialog (WorkoutDetailDialog, GeneratePlanDialog, SchedulePlanDialog, RenamePlanDialog, GoalDialog, CombineWorkoutsDialog) should return focus to the element that opened it.
  • After using the skip-to-content link, #main-content should receive focus.

How to file results

When you run any of the above, either:

  1. Comment on this issue with the findings (page, element, expected vs actual, severity), or
  2. Open a follow-up PR referencing this issue for each distinct fix.

Short fixes (color swaps, missing aria-label, focus-return) are fine to batch into one "a11y fixes from manual pass" PR. Anything structural (e.g. the virtualized Timeline announcement strategy) should be a standalone issue or PR.

Related

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions