WCAG 2.2 AA Audit Checklist
Tracks NFR-07 / NFR-08. Re-run before every release. Findings go into
CHANGELOG.md under the release that fixes them.
Tooling:
- axe-core (manual + future CI gate) — automated catches ~40% of issues.
- Keyboard-only walkthrough — no mouse, every flow.
- VoiceOver (macOS / iOS) + NVDA (Windows) spot checks.
- prefers-reduced-motion + prefers-contrast OS toggles.
- Mobile 430×697 viewport (our design target).
Status legend: ✅ pass · 🚧 finding open · ⏳ not yet audited
Per-route checklist
| Route | Initial | Keyboard | Screen reader | Reduced motion | Contrast |
|---|---|---|---|---|---|
/ |
✅ | ✅ | ✅ | ✅ | ✅ |
/trust |
✅ | ✅ | ⏳ | ✅ | ⏳ |
/login |
✅ | ✅ | ✅ | ✅ | ⏳ |
/signup |
✅ | ✅ | ✅ | ✅ | ⏳ |
/forgot-password |
✅ | ✅ | ⏳ | ✅ | ⏳ |
/reset-password |
✅ | ✅ | ⏳ | ✅ | ⏳ |
/_authenticated/home |
✅ | ✅ | ⏳ | ✅ | ⏳ |
/_authenticated/new |
✅ | ✅ | ⏳ | ✅ | ⏳ |
/_authenticated/c/$id |
✅ | ✅ | ⏳ | ✅ | ⏳ |
/_authenticated/settings |
✅ | ✅ | ⏳ | ✅ | ⏳ |
Cross-cutting checks
Perceivable
- 1.1.1 Non-text content — every meaningful image has
alt; logo decorativealt="". - 1.3.1 Info and relationships — semantic landmarks (
<main>,<nav>,<header>,<footer>) on every route. - 1.4.3 Contrast (minimum) — body text ≥ 4.5:1, large text ≥ 3:1
against the deep-purple ground in both default and
prefers-contrast: more. - 1.4.4 Resize text — no clipping at 200% browser zoom.
- 1.4.10 Reflow — no horizontal scroll at 320 CSS px width.
- 1.4.11 Non-text contrast — focus rings, switch borders, gold accents ≥ 3:1 against background.
- 1.4.12 Text spacing — layout survives bumped letter/line spacing.
- 1.4.13 Content on hover or focus — tooltips dismissible, hoverable, persistent.
Operable
- 2.1.1 Keyboard — every interactive element reachable via Tab.
- 2.1.2 No keyboard trap — modals, drawers, the composer all release.
- 2.4.3 Focus order — visual order matches DOM order.
- 2.4.7 Focus visible — every focusable element shows a ring with ≥ 3:1 contrast.
- 2.4.11 Focus not obscured (minimum) — no sticky header hides focused inputs (especially the composer at 430px).
- 2.5.5 Target size (enhanced — 2.2) — interactive targets ≥ 24×24 CSS px, ideally 44×44 on touch.
- 2.5.7 Dragging movements (2.2) — no drag-only interactions; the attachment picker has a tap fallback.
- 2.5.8 Target size (minimum — 2.2) — confirm message bubbles' action menu meets minimum.
- 3.2.6 Consistent help (2.2) —
/trustlink reachable from every page in the same place. - 3.3.7 Redundant entry (2.2) — signup never re-asks for email.
- 3.3.8 Accessible authentication (minimum — 2.2) — recovery code can be pasted; no cognitive puzzle required.
Understandable
- 3.1.1 Language of page —
<html lang="en">set. - 3.3.1 Error identification — every form error names the field and the fix.
- 3.3.2 Labels or instructions — recovery code field explains the 24-char format and the dash convention.
Robust
- 4.1.2 Name, role, value — switch components expose
role="switch"+aria-checked. shadcn/ui defaults verified. - 4.1.3 Status messages — toast notifications use
aria-live.
Motion
- All decorative animations respect
prefers-reduced-motion: reduce. - Send-message bubble entrance is replaced with a fade for reduced motion.
- No auto-playing video or parallax.
Open findings
(none yet — initial pass scheduled)
2026-05-09 — first pass
Fixed
- F-01 / 2.3.3 Reduced motion — global
prefers-reduced-motion: reducerule insrc/styles.csscollapses all decorative animations (logo breathe, fade-up entrance, pulse-ring) to ~0ms and forces fade-up content to its final visible state so nothing stays invisible. - F-02 / 2.4.1 Bypass blocks — added a focus-revealed
"Skip to content" link in
src/routes/__root.tsx. Targets#main-contenton every route shell. - F-03 / 1.3.1 Landmarks —
/,/login,/signup,/404now expose a single<main id="main-content">landmark.
Open
- F-04 / 1.4.3 Contrast — measured 2026-05-09 with proper sRGB
conversion. Body text 15.4:1, gold accents 9.6:1, muted-foreground 5.3:1,
gold-dark 5.3:1 — all pass AA. Fixed:
--inputraised from 1.32:1 to 3.21:1 (oklch 0.50) so form-control borders meet WCAG 1.4.11.--borderkept subtle (1.32:1) since it's only used on decorative dividers. - F-05 / 2.4.7 Focus visible — confirm the rounded gold inputs render a visible ring on Safari iOS (focus-visible quirks).
- F-06 / 2.5.5 Target size (2.2 AA) — composer send button is 32×32; bump to ≥ 24×24 with adequate spacing or to 44×44 on touch.
- F-07 /
_authenticated/*routes — entire authed surface still ⏳.
