feat(a11y): add ARIA labels, keyboard nav, and screen reader support#57
feat(a11y): add ARIA labels, keyboard nav, and screen reader support#57
Conversation
First accessibility pass covering foundation, landmarks, shared components, and sidebar navigation. Adds focus-visible rings, skip-nav link, dialog focus trap, progressbar roles, landmark regions, flyout menu keyboard navigation (Arrow/Home/End/Escape), and axe-core dev integration. All changes are additive ARIA attributes with no logic regressions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 59cdf3811a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Move skip-nav link from index.html into AppShell with a JS click handler so it doesn't rewrite the hash fragment and break routing. Restore focus to the triggering element when ConfirmDialog closes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6350d352b6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…rent nav items Give each nested route its own distinct window title instead of the parent section name, so screen readers and the OS task switcher show the actual page. Only apply aria-current="page" to leaf nav items, not to flyout parent triggers that merely contain the active child. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7cd3753ebe
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| switch (e.key) { | ||
| case 'ArrowDown': | ||
| e.preventDefault() | ||
| menuItems[(currentIndex + 1) % menuItems.length].focus() | ||
| break |
There was a problem hiding this comment.
Close the flyout menu when Tab leaves it
When a submenu is open, focus is moved into the popup, but handleKeyDown only handles arrows/Home/End/Escape. Because the items are still tabbable <button role="menuitem"> elements and there is no blur handler, pressing Tab or Shift+Tab moves focus elsewhere while the flyout stays mounted and the trigger keeps aria-expanded=true, leaving keyboard users with an orphaned popup on screen.
Useful? React with 👍 / 👎.
Summary
prefers-reduced-motionmedia query,@axe-core/reactdev integration<main id="main-content">,<nav aria-label>, nav grouprole="group"witharia-labelledby, dynamicdocument.titleon route change (localized via i18n)role="dialog", focus trap, Escape dismiss), ScanProgress (role="progressbar",aria-live), HealthScore (role="img"), ErrorAlert (role="alert"), StatCard (role="group"), EmptyState (hidden decorative icon)aria-current="page",aria-expanded/aria-haspopupfor flyout triggers,role="menu"/role="menuitem"on flyouts, full keyboard nav (Arrow/Home/End/Escape), auto-focus on openrole="toolbar",aria-labelon minimize/maximize/close buttonsrole="status", labeled dismiss buttonTest plan
npm test— 767/767 tests pass, no regressionsnpm run dev+ open DevTools console: verify axe-core reports are present🤖 Generated with Claude Code