You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Accessibility is a first-class requirement. Every PR that touches UI must pass the checks below before merge.
6
+
7
+
### Automated axe checks (CI)
8
+
9
+
The `accessibility` CI job runs `@axe-core/playwright` against the quote, policy, claims, and vote routes. **No critical violations are permitted.** The job uploads a Playwright report as an artifact on failure.
10
+
11
+
Run locally:
12
+
13
+
```bash
14
+
cd frontend
15
+
npm install
16
+
npm run build
17
+
npx playwright test tests/accessibility.spec.ts
18
+
```
19
+
20
+
### Manual axe spot-check
21
+
22
+
1. Install the [axe DevTools browser extension](https://www.deque.com/axe/devtools/).
23
+
2. Open each targeted route: `/quote`, `/policy`, `/claims`, `/claims/<id>`.
24
+
3. Run the full-page scan. Resolve any **critical** or **serious** violations before opening a PR.
25
+
26
+
### Keyboard-only walkthrough
27
+
28
+
Verify these flows using only the keyboard (no mouse):
29
+
30
+
| Flow | Steps |
31
+
|------|-------|
32
+
| Get a quote | Tab through all form fields → submit → confirm quote preview updates |
33
+
| Purchase policy | Complete all 4 wizard steps using Tab / Shift+Tab / Enter / Space |
34
+
| File a claim | Complete all 4 wizard steps; confirm focus moves to new step heading on advance |
35
+
| Cast a vote | Tab to Approve / Reject buttons → Enter to open confirm modal → Tab within modal → confirm or cancel |
36
+
| Connect wallet | Tab to "Connect Wallet" button → Enter → confirm status announced |
37
+
38
+
Focus must always be visible. After a modal opens, focus must move inside it. After a modal closes, focus must return to the trigger.
39
+
40
+
### Screen reader spot-check (per major release)
41
+
42
+
Test at minimum one major flow per release with a screen reader:
43
+
44
+
-**macOS / iOS**: VoiceOver (`Cmd+F5` to toggle)
45
+
-**Windows**: NVDA (free) or Narrator
46
+
-**Android**: TalkBack
47
+
48
+
Checklist:
49
+
-[ ] Transaction status updates are announced (aria-live regions on wizard and policy pages)
50
+
-[ ] Step changes in wizards are announced (focus moves to hidden `<h2>` with step name)
51
+
-[ ] Quote preview updates are announced on the quote page
52
+
-[ ] Vote tally countdown is announced via `aria-live="polite"`
53
+
-[ ] Modal title is read when dialog opens
54
+
-[ ] Icon-only buttons have accessible names (aria-label or sr-only text)
55
+
-[ ] Claim status badges convey outcome via text/shape, not color alone
56
+
57
+
### Reduced-motion
58
+
59
+
Verify that setting `prefers-reduced-motion: reduce` (OS accessibility setting or DevTools emulation) stops all non-essential animations. Loading spinners should become static; slide/fade transitions should be instant.
60
+
61
+
### Heading hierarchy
62
+
63
+
Each page must have exactly one `<h1>`. Use the browser Accessibility Tree panel (DevTools → Accessibility) or the [HeadingsMap extension](https://rumoroso.bitbucket.io/headingsmap/) to verify a logical heading order with no skipped levels.
64
+
65
+
### Landmarks
66
+
67
+
Every page must have at minimum: `<main>`, `<nav>` (if navigation present), and `<footer>` (if present). Verify with the Accessibility Tree or axe.
68
+
69
+
### Color contrast
70
+
71
+
All text must meet WCAG AA contrast ratios (4.5:1 normal text, 3:1 large text). Use the axe scan or the browser color-contrast checker. Claim outcomes (Approved / Rejected / Pending) must not rely on color alone — shape indicators and text labels are required.
72
+
73
+
### Adding new UI
74
+
75
+
When adding new interactive components:
76
+
77
+
1. Icon-only controls **must** have `aria-label` or a visually hidden label.
78
+
2. Async state changes (transactions, loading) **must** update an `aria-live` region.
79
+
3. Multi-step wizards **must** move focus to a step heading on step change.
80
+
4. Modals **must** trap focus and return it to the trigger on close (Radix Dialog handles this automatically).
81
+
5. Animations **must** respect `prefers-reduced-motion` via the global CSS rule in `globals.css`.
0 commit comments