From 3178d65b35478d33a37c6b0f2cb99ef0d21c80d8 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 18 Sep 2026 06:08:16 +0000 Subject: [PATCH] feat(a11y): RGAA 4.1 audit report, axe e2e gate and first fixes Adds docs/ACCESSIBILITY.md (findings F1-F10 mapped to RGAA 4.1 and WCAG 2.1, what passed, what tooling cannot test) and tests/e2e/accessibility.spec.ts, which runs axe-core WCAG 2.x A/AA scans over every screen in both themes plus scripted checks for titles, landmarks, skip link, focus visibility and keyboard reach. The spec is red until the remaining fixes land. Fixes that need no design sign-off: - F1: the tooltip action now exposes its text as aria-label on icon-only controls; removing `title` had left ~570 buttons unnamed. - F5/F6/F7: checklist checkboxes and inputs, the editor textbox and the users-page role select get accessible names. - F8: scrollable code blocks are keyboard reachable (tabindex=0), except in card previews which clip instead of scroll. - F9: each settings tab has its own page title. - F10: skip link to main content. Still open, pending sign-off: F2 colour tokens, F3 card structure, F4 focus-visible styling. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_014u5WimmZvshm9RJSd1c6wx --- docs/ACCESSIBILITY.md | 144 ++++++++++ package.json | 1 + pnpm-lock.yaml | 19 ++ src/lib/components/Checklist.svelte | 5 +- src/lib/components/NoteCard.svelte | 4 +- src/lib/components/TiptapEditor.svelte | 2 + src/lib/utils/markdown.ts | 21 +- src/lib/utils/tooltip.ts | 11 + src/routes/(app)/+layout.svelte | 10 +- src/routes/(app)/settings/+layout.svelte | 3 - src/routes/(app)/settings/about/+page.svelte | 4 + src/routes/(app)/settings/mcp/+page.svelte | 4 + .../(app)/settings/preferences/+page.svelte | 4 + .../(app)/settings/profile/+page.svelte | 4 + src/routes/(app)/settings/users/+page.svelte | 5 + tests/e2e/accessibility.spec.ts | 252 ++++++++++++++++++ 16 files changed, 484 insertions(+), 9 deletions(-) create mode 100644 docs/ACCESSIBILITY.md create mode 100644 tests/e2e/accessibility.spec.ts 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 `