Files

54 lines
1.9 KiB
Markdown

# Rule: accessibility
## The gate is a keyboard, not a scanner
Automated tooling catches roughly **57%** of accessibility defects, and the
misses cluster exactly where usability is decided: focus visibility, focus
obscured by sticky elements, target size, and drag alternatives. Run axe, then
do the manual sweep anyway.
**Manual sweep, every interactive component:**
1. Tab through it. Every control reachable, in a sensible order.
2. Focus ring visible at every stop — this site uses
`outline: 3px solid #a7483f; outline-offset: 2px`. Keep it.
3. Operate it with Enter and Space. Escape closes anything that opened.
4. Nothing is reachable only by hover or only by pointer.
5. Zoom to 200%. Nothing clipped, nothing overlapping.
## Semantics
- Native elements first. `<button>` for actions, `<a>` for navigation. A `<div>`
with a click handler is a defect, not a style choice.
- ARIA only when HTML cannot express it. The current code does this well —
`role="tablist"`, `aria-pressed`, `aria-current="page"`, `aria-label` on
regions. Preserve every one during migration; they are asserted in
`verify.mjs`.
- One `<h1>` per page. Heading levels never skip.
- Every image needs `alt`. Decorative images get `alt=""`.
## State
Interactive state must be exposed, not just painted:
```html
<!-- wrong: only colour says it is selected -->
<button class="active">Improved draft</button>
<!-- right -->
<button class="active" aria-pressed="true">Improved draft</button>
```
The vote widget and preview switcher already do this. Match them.
## Contrast
Body text ≥ 4.5:1, large text ≥ 3:1, UI boundaries ≥ 3:1. Check any new
combination against the token palette — `--muted` on `--paper` is the pair most
likely to fail; verify before shipping.
## Bilingual content
`<html lang>` must change with the language toggle, not just the text. Screen
readers pick pronunciation from it. This already works today — do not regress
it.