Skip to content

Commit 635376c

Browse files
Merge pull request #106 from Pee-pheelips/feature/accessibility-a11y
Feature/accessibility a11y
2 parents fdac284 + 89b7b04 commit 635376c

38 files changed

Lines changed: 2308 additions & 1214 deletions

docs/ACCESSIBILITY.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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.

package-lock.json

Lines changed: 95 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@
6969
"@types/node": "^20",
7070
"@types/react": "^18.3.27",
7171
"@types/react-dom": "^18.3.7",
72-
"@types/socket.io-client": "^1.4.36",
7372
"@vitejs/plugin-react-swc": "^3.10.2",
7473
"eslint": "^9",
7574
"eslint-config-next": "15.3.1",

src/app/layout.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import PerformanceMonitor from '@/components/performance/PerformanceMonitor';
1010
import PrefetchingEngine from '@/components/performance/PrefetchingEngine';
1111
import StateManagerIntegration from '@/components/state/StateManagerIntegration';
1212
import { PWAManager } from '@/components/pwa/PWAManager';
13+
import { AccessibilityProvider } from '@/components/accessibility/AccessibilityProvider';
1314

1415
const geistSans = Geist({
1516
// ...
@@ -42,13 +43,15 @@ export default function RootLayout({
4243
<InternationalizationEngine>
4344
<CulturalAdaptationManager>
4445
<ThemeProvider>
45-
<OfflineModeProvider>
46-
<PWAManager />
47-
<StateManagerIntegration />
48-
<PerformanceMonitor />
49-
<PrefetchingEngine />
50-
{children}
51-
</OfflineModeProvider>
46+
<AccessibilityProvider pageLabel="TeachLink — main application">
47+
<OfflineModeProvider>
48+
<PWAManager />
49+
<StateManagerIntegration />
50+
<PerformanceMonitor />
51+
<PrefetchingEngine />
52+
{children}
53+
</OfflineModeProvider>
54+
</AccessibilityProvider>
5255
</ThemeProvider>
5356
</CulturalAdaptationManager>
5457
</InternationalizationEngine>

0 commit comments

Comments
 (0)