feat: add accessibility support (ARIA labels, semantic HTML, keyboard nav)#8
feat: add accessibility support (ARIA labels, semantic HTML, keyboard nav)#8
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds comprehensive accessibility support to the workflow-ui library, including ARIA labels, semantic HTML, keyboard navigation, and automated a11y testing. The PR introduces 6 new reusable UI components (StatusBadge, DataTable, Modal, LoadingSpinner, ErrorBoundary, Sidebar) and enhances the existing LoginPage with proper accessibility attributes. However, there's a critical issue with keyboard focus indicators that prevents the accessibility improvements from being fully effective.
Changes:
- Added vitest-axe for automated accessibility testing with configurable test helpers
- Enhanced LoginPage with semantic HTML (
<main>landmark), connected labels, ARIA attributes (aria-live, aria-busy, aria-required, aria-describedby), and keyboard focus indicators (though these have implementation issues) - Created 6 new UI components with comprehensive test coverage (except Sidebar which lacks tests)
Reviewed changes
Copilot reviewed 19 out of 20 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| package.json | Added vitest-axe dependency, updated eslint-plugin-react-refresh to ^0.5.1, added ./components export |
| package-lock.json | Lock file updates for new dependencies |
| vite.config.ts | Added components/index entry point for library build |
| src/test/setup.ts | Registered vitest-axe matchers globally |
| src/test/a11y.ts | Created checkA11y() helper for accessibility testing |
| src/index.ts | Exported all new components and their types from components module |
| src/components/index.ts | Created barrel export for all UI components |
| src/components/StatusBadge.tsx | New badge component with status indicators and pulsing animation |
| src/components/StatusBadge.test.tsx | Comprehensive tests for StatusBadge component |
| src/components/Sidebar.tsx | New navigation sidebar with collapse/expand, ARIA labels, and keyboard support |
| src/components/Modal.tsx | New modal dialog with proper ARIA attributes, focus trap, and keyboard support |
| src/components/Modal.test.tsx | Comprehensive tests for Modal including accessibility checks |
| src/components/LoadingSpinner.tsx | New loading spinner with aria-live status announcements |
| src/components/LoadingSpinner.test.tsx | Tests for LoadingSpinner including ARIA attributes |
| src/components/ErrorBoundary.tsx | New error boundary component with role="alert" |
| src/components/ErrorBoundary.test.tsx | Comprehensive tests for ErrorBoundary error handling |
| src/components/DataTable.tsx | New data table with sorting, pagination, and ARIA attributes |
| src/components/DataTable.test.tsx | Comprehensive tests for DataTable functionality |
| src/auth/LoginPage.tsx | Enhanced with semantic HTML, ARIA attributes, live regions, and keyboard focus indicators (with implementation issues) |
| src/auth/LoginPage.test.tsx | Added 18 new accessibility tests covering axe violations, ARIA attributes, and keyboard navigation |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| width: '100%', | ||
| padding: '10px', | ||
| fontSize: '15px', | ||
| opacity: loading ? 0.7 : 1, |
There was a problem hiding this comment.
Setting outlineColor alone will not produce a visible keyboard focus indicator on the submit button. The button needs an explicit outline style such as outline: '2px solid' in addition to outlineColor to ensure keyboard users can see focus indication (WCAG 2.1 AA compliance).
| opacity: loading ? 0.7 : 1, | |
| opacity: loading ? 0.7 : 1, | |
| outline: '2px solid', |
| style={{ | ||
| ...baseStyles.input, | ||
| outlineColor: colors.blue, | ||
| }} |
There was a problem hiding this comment.
Setting outlineColor will not produce a visible keyboard focus indicator because baseStyles.input has outline: 'none'. To achieve WCAG 2.1 AA compliant keyboard focus indicators, you need to either remove the outline: 'none' from baseStyles.input or override it with a valid outline style (e.g., outline: '2px solid', outlineColor: colors.blue) instead of just setting outlineColor.
| style={{ | ||
| ...baseStyles.input, | ||
| outlineColor: colors.blue, | ||
| }} |
There was a problem hiding this comment.
Setting outlineColor will not produce a visible keyboard focus indicator because baseStyles.input has outline: 'none'. To achieve WCAG 2.1 AA compliant keyboard focus indicators, you need to either remove the outline: 'none' from baseStyles.input or override it with a valid outline style (e.g., outline: '2px solid', outlineColor: colors.blue) instead of just setting outlineColor.
- Add ARIA labels, roles, and semantic HTML to LoginPage component - Add aria-busy, aria-disabled, and aria-live for loading states - Add useId() for stable form element ID associations - Add vitest-axe for automated accessibility testing - Add checkA11y() test helper with axe-core integration - Add 18 new accessibility-focused tests for LoginPage Closes #4 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
b15914e to
e1335bc
Compare
Summary
<div>with<main>landmark inLoginPagefor proper page structure<label>elements to inputs viahtmlFor/idpairs usinguseId()for stable unique IDsaria-required="true"to required fields (reinforces nativerequiredattribute)aria-label="Login form"to<form>for a named form landmarkaria-busyon the form andaria-disabledon the button during async loginaria-live="polite"+aria-atomic="true"region for screen-reader announcementsrole="alert"to error message div for immediate announcement on erroraria-describedbyon inputs pointing to the error element when an error is shownoutlineColoron inputs and button for visible keyboard focus indicator (WCAG 2.1 AA)vitest-axeand registertoHaveNoViolationsmatcher in test setupsrc/test/a11y.tshelper withcheckA11y()utilityErrorBoundarytest (broken TDZ reference in Wrapper component)LoadingSpinnertest (wrong CSS selector for ring element)Closes #4
Test plan
🤖 Generated with Claude Code