diff --git a/docs/ACCESSIBILITY.md b/docs/ACCESSIBILITY.md new file mode 100644 index 0000000..900ea74 --- /dev/null +++ b/docs/ACCESSIBILITY.md @@ -0,0 +1,144 @@ +# Accessibility audit — RGAA 4.1 + +**Date:** 2026-09-17 · **Build audited:** `main` at `7bf06ed` (before #91) · **Status:** findings open, fixes in progress on `feat/a11y-audit` + +RGAA 4.1 (Référentiel général d'amélioration de l'accessibilité) is the French application of WCAG 2.1 level AA: 106 criteria in 13 topics. This document records what was tested, what failed, what passed, and what has not been tested. It is **not** a compliance declaration: roughly a third of RGAA can be verified by tooling and scripted checks, and the remainder needs a manual pass with assistive technology (see [Not tested](#not-tested)). + +## Method + +| Layer | Tool | What it covers | +|---|---|---| +| Automated rules | axe-core 4.13 via `@axe-core/playwright`, rule tags `wcag2a`, `wcag2aa`, `wcag21a`, `wcag21aa` | Contrast, names and roles, form labels, language, nesting, scroll regions, ARIA validity | +| Scripted checks | `tests/e2e/accessibility.spec.ts` | Page titles, landmarks, skip link, keyboard reach, visible focus, Escape handling, card action naming | +| Manual reading | Source review of the components the scans pointed at | Root causes and fix proposals | + +Screens scanned, each in **light and dark** theme: notes grid seeded with all 12 note colours plus checklist, tagged and code notes; open editor; editor with colour picker; share dialog; editor on a checklist note; archive; trash; tag view; search results; settings (profile, preferences, MCP, users, about); login; public share page. + +The spec doubles as the CI gate: every scan asserts zero violations and writes its raw axe output to `test-results/a11y/-.json`. Run it alone with: + +``` +pnpm playwright test accessibility.spec.ts +``` + +## Findings + +Severity: **Blocking** = prevents a group of users from completing a core task · **Major** = criterion fails on most screens · **Minor** = localised failure. + +Counts are raw axe node counts across all scans; the grid repeats every card, so one root cause shows up as hundreds of nodes. + +### F1 · Icon buttons have no accessible name — Blocking + +- **RGAA 11.9** (button label relevance), **RGAA 7.1** (script compatibility with assistive tech) · WCAG 4.1.2 +- **Where:** card actions (pin, archive, trash, restore, delete) on every card; editor footer (colour picker, image, share, archive, overflow menu); new-checklist button. 574 nodes on 7 screens, both themes. +- **Evidence:** axe `button-name`: "Element does not have inner text that is visible to screen readers; aria-label attribute does not exist or is empty". +- **Root cause:** `src/lib/utils/tooltip.ts` was introduced as a drop-in replacement for the `title` attribute. It renders a visual tooltip but sets no `aria-label`, so every icon-only button that adopted it lost its name for screen readers. +- **Fix:** the tooltip action sets `aria-label` from its text when the element has no accessible name of its own. One change repairs every instance. + +### F2 · Insufficient text contrast — Blocking (light theme), Minor (dark theme) + +- **RGAA 3.2** (text contrast), **RGAA 3.3** (UI component contrast) · WCAG 1.4.3, 1.4.11 +- **Where and how far off** (AA requires 4.5:1 for body text, 3:1 for large text and component boundaries): + +| Element | Colours | Ratio | Screens | +|---|---|---|---| +| Brand title "Crumbs" (18px) | `#C8860A` on `#faf5eb` | 2.81 | all app screens, login | +| Active sidebar item | `#C8860A` on gold wash `#f3e4c9` | 2.43 | all app screens | +| Active settings tab | `#C8860A` on `#ead8b5` | 2.18 | all settings tabs | +| Links inside note previews | `#C8860A` on each note colour | 1.46 (peach) to 2.81 (default) | notes, archive, trash, search, editor | +| Note preview body text | muted `#6b6272` on coral / peach / storm / dusk / sage / fog | 3.25 / 2.77 / 3.44 / 3.38 / 3.93 / 4.45 | same | +| Primary buttons (login, save, invite, tag filter) | white on `#C8860A` | 3.05 | login, settings, tag | +| Primary buttons, dark theme | white on `#e0a030` | 2.27 | login, settings, tag | + +- **What passes:** in dark mode every note colour passes for both `--text` (worst 9.3:1 on mint) and `--text-muted` (worst 4.9:1 on mint). The code-block palette added in #91 passes by construction (unit-tested). The only dark-theme failure is white text on gold buttons. +- **Root cause:** the brand gold `--primary` is used for text, not only for accents, and it is too light on parchment. `--text-muted` was tuned for `--bg-surface` and never checked against the coloured cards. +- **Fix (needs design sign-off, see below):** + 1. Add `--primary-text`, a darker gold for text and icons on light chrome (`#7d5306` is already in use as the code keyword colour; it reaches 6.2:1 on `--bg-surface`, 5.5:1 on `--bg-base`, 5.4:1 on the sidebar's active wash and 4.8:1 on the settings tab wash). Dark theme keeps `#e0a030`, which passes on every dark surface (worst 5.2:1 on mint). Brand title, sidebar and settings active states switch to it. `--primary` stays for borders, shadows, washes and the checkbox accent. + 2. Links inside note content use `--text` with an underline instead of gold. No gold passes on the coral, peach, sage, storm or dusk cards (`#7d5306` bottoms out at 3.2:1 on peach), and the underline keeps links identifiable without relying on colour (RGAA 3.1). + 3. Primary buttons use ink text (`--text`) on gold in both themes: 5.6:1 light, 7.5:1 dark. + 4. Note preview body text uses `--text` instead of `--text-muted`. It is the note's content, not secondary information, and `--text` passes on every card colour (worst 8.2:1 on peach). + +### F3 · Note cards nest interactive controls and expose two tab stops per card — Blocking + +- **RGAA 7.1**, **RGAA 12.8** (tab order), **RGAA 7.3** (keyboard operability) · WCAG 4.1.2, 2.1.1, 2.4.3 +- **Where:** every card on notes, archive, trash, tag and search screens. 182 axe `nested-interactive` nodes. +- **Evidence:** the card is `
` and contains the action `