|
| 1 | +# Accessibility on TeachLink |
| 2 | + |
| 3 | +This app ships a layered accessibility toolkit aimed at **WCAG 2.1 Level AA** patterns. Automated checks catch common failures; they do **not** replace testing with screen readers and keyboard-only navigation. |
| 4 | + |
| 5 | +## Architecture |
| 6 | + |
| 7 | +| Piece | Role | |
| 8 | +| --- | --- | |
| 9 | +| `AccessibilityProvider` | Global context: `announce`, motion preference, keyboard modality, `runPageAudit`. | |
| 10 | +| `ScreenReaderSupport` | Permanent **polite** and **assertive** live regions for reliable announcements. | |
| 11 | +| `KeyboardNavigation` | **Alt+M** focuses main content; **Shift+?** opens a shortcuts dialog (focus-trapped). Toolbar **roving** focus with `[data-roving-root]`. | |
| 12 | +| `AccessibilityAudit` | Dev-only (by default) floating panel with heuristic DOM checks. | |
| 13 | +| `useAccessibility()` | Reads context, or a safe fallback when used outside the provider. | |
| 14 | +| `accessibilityUtils` | Focus helpers, contrast math, `checkAccessibilityIssues`, `runAccessibilityAudit`. | |
| 15 | + |
| 16 | +## Using the provider |
| 17 | + |
| 18 | +The root layout wraps the app with `AccessibilityProvider`. Pass `enableDevAudit={false}` in production if you want to hide the audit FAB entirely, or set `NODE_ENV` so the default dev panel is off. |
| 19 | + |
| 20 | +## Announcements |
| 21 | + |
| 22 | +```tsx |
| 23 | +import { useAccessibility } from '@/hooks/useAccessibility'; |
| 24 | + |
| 25 | +function SaveButton() { |
| 26 | + const { announce } = useAccessibility(); |
| 27 | + return ( |
| 28 | + <button type="button" onClick={() => announce('Changes saved', 'polite')}> |
| 29 | + Save |
| 30 | + </button> |
| 31 | + ); |
| 32 | +} |
| 33 | +``` |
| 34 | + |
| 35 | +Use **assertive** only for urgent errors or time-sensitive status. |
| 36 | + |
| 37 | +## Keyboard and landmarks |
| 38 | + |
| 39 | +- Give the primary `<main>` a stable id such as `main-content` so skip links and **Alt+M** work everywhere. There should be **exactly one** `<main>` (or `role="main"`) per view. |
| 40 | +- For horizontal toolbars, add `data-roving-root` on the toolbar container. **Left/Right arrow** moves among buttons, links, tabs, and elements marked with `data-roving-item` (including those using `tabindex="-1"` for roving patterns). |
| 41 | + |
| 42 | +## What automation does *not* prove |
| 43 | + |
| 44 | +- **WCAG 2.1 AA** for the whole product requires page-by-page review (contrast in context, timing, reflow, errors, etc.). |
| 45 | +- The audit panel and `checkAccessibilityIssues` only flag **some** DOM patterns. They miss false positives/negatives and cannot judge screen reader UX. |
| 46 | +- **All keyboard paths** and **all screen reader announcements** still need manual QA on real flows. |
| 47 | + |
| 48 | +## ARIA checklist (authoring) |
| 49 | + |
| 50 | +1. Every interactive control has a computed **accessible name** (visible text, `aria-label`, or `aria-labelledby`). |
| 51 | +2. Form fields are labeled with a wrapping `<label>`, `<label htmlFor>`, or `aria-label` / `aria-labelledby`. |
| 52 | +3. Images convey meaning with `alt`; decorative images use `alt=""`. |
| 53 | +4. Headings describe structure without skipped levels. |
| 54 | +5. Expandable regions use `aria-expanded`; dialogs use `role="dialog"`, `aria-modal="true"`, and initial focus management. |
| 55 | +6. Prefer native `<button>` and `<a href>` over generic elements with scripts. |
| 56 | + |
| 57 | +## Testing |
| 58 | + |
| 59 | +- Navigate the primary tasks **without a mouse** (including modals, forms, and media). |
| 60 | +- Run **VoiceOver** (macOS) or **NVDA** (Windows) on critical flows; verify focus order and live region behavior. |
| 61 | +- Use the in-app **Accessibility audit** (development) to catch missing `alt`, labels, names, landmarks, `lang`, and duplicate `id`s—then fix and re-test manually. |
| 62 | + |
| 63 | +For more examples, see `src/app/components/accessibility/README.md` and `ACCESSIBILITY_IMPLEMENTATION_GUIDE.md` in the repo root. |
0 commit comments