From 54b3ba867f397f63302766261a16ca72d0025a63 Mon Sep 17 00:00:00 2001 From: dev_agent Date: Sat, 10 Oct 2026 23:43:51 -0400 Subject: [PATCH] Accessibility pass over every route Record the pass in docs/A11Y.md: keyboard, focus, names and roles, text contrast per token pair, hit targets and reduced motion for each route. Fix the four medium findings, each with a regression test: - Muted text in neutral-600 (3.5-4.0:1) is now neutral-700; a contrast test checks every text token pair and the tokens stylesheets use. - The bottom bar's focus ring is on-accent, not grape on the dark bar. - The Kitchen reading text-size ring sits inside the clipped pill. - The tablet-portrait Quest log chips keep the 44 px tap minimum. Low findings are listed as follow-ups. Co-Authored-By: Claude Opus 5.5 --- docs/A11Y.md | 113 ++++++++++++++++++++ web/src/contrast.test.ts | 86 +++++++++++++++ web/src/dinners/DinnerDetailPage.module.css | 11 +- web/src/dinners/KitchenReading.test.tsx | 17 ++- web/src/ideas/CookFromPantryPage.module.css | 2 +- web/src/plan/PlanPage.module.css | 2 +- web/src/plan/PlanPage.test.tsx | 6 +- web/src/plan/PlanPanel.module.css | 5 + web/src/plan/SwapSheet.module.css | 2 +- web/src/shell/BottomBar.module.css | 6 ++ web/src/shell/BottomBar.test.tsx | 12 +++ 11 files changed, 255 insertions(+), 7 deletions(-) create mode 100644 docs/A11Y.md create mode 100644 web/src/contrast.test.ts diff --git a/docs/A11Y.md b/docs/A11Y.md new file mode 100644 index 0000000..8d986f5 --- /dev/null +++ b/docs/A11Y.md @@ -0,0 +1,113 @@ +# Accessibility pass + +A pass over every route the PWA serves (the routes in [STATES.md](STATES.md)) +for launch, on 2026-10-10. Each route was checked for: + +- **Keyboard** — every action reachable and operable with the keyboard alone. +- **Focus** — a visible focus ring on everything that takes focus. +- **Names and roles** — what a screen reader announces: switches, checkboxes, + the current nav item, dialogs and their labels. +- **Contrast** — text at least 4.5:1 against its ground, for every token pair + used for text. +- **Hit targets** — every tap target at least 44 × 44 px. +- **Reduced motion** — what `prefers-reduced-motion: reduce` turns off. + +The pass read each screen's markup and stylesheets, computed contrast from +[tokens.css](../web/src/styles/tokens.css), and checked the fixes' computed +styles in Chrome. Severity: **high** blocks a task for keyboard or +screen-reader users; **medium** fails a criterion above but leaves a way +through; **low** is a gap outside the criteria above, or a polish item. High +and medium findings are fixed in this pass with a regression test; low ones are +follow-ups. + +## Shared building blocks + +Most of the criteria are met once, in the shared components every screen uses: + +| Concern | Where | Behaviour | +| --- | --- | --- | +| Focus ring | [base.css](../web/src/styles/base.css) | `:focus-visible` draws a 2 px grape (`--color-accent`) outline 2 px out, on every focusable element. | +| Dialogs | [Sheet](../web/src/ui/Sheet.tsx) | `role="dialog"`, `aria-modal="true"`, and a required `aria-label` or `aria-labelledby` (the type will not compile without one). Opening moves focus in, Tab is trapped, Escape and the scrim close, the page behind is `inert`, and focus returns to the opener. Every sheet is labelled by its visible title. | +| Toggles | [Toggle](../web/src/ui/Toggle.tsx) | A `