---
name: velin-a11y-page-checklist
description: VelinStyle page a11y checklist; review-only.
---
# A11y Page Checklist

## Purpose
Runs page-level accessibility checklist before release.

## Discovery
Router intent `review-a11y` (accessibility / Barrierefreiheit / a11y / Fokus+Tastatur) should land here (or focus-keyboard for keyboard-only asks). Always **VALIDATE** — never Write/Apply.

## Domain
R · category `accessibility`

## Capabilities
- `review.a11y`

## Permission
`review-only`

## Allowed actions
- READ
- ANALYZE

## Forbidden actions
- WRITE
- APPLY
- ROLLBACK
- ACCEPT
- DEFER
- NOTE
- ACKNOWLEDGE
- CATALOG
- MANIFEST
- CLASSIFY
- invent-target

## Stop conditions
- SR-11

## Agent role / risk
Reviewer · risk `low`

## Workflow
1. ANALYZE/VALIDATE only (`permission: review-only`).
2. Apply checklist below.
3. Unclear → STOP (SR-11/SR-03 as listed on skill).
4. Never rewrite-Apply, invent targets, or skip reuse on build handoffs.

## Checklist
- [ ] Labels/focus/contrast (BP-A01)
- [ ] Landmarks/headings
- [ ] `aria-controls` / `aria-labelledby` / `aria-describedby` targets exist (and are unique — `idref-ambiguous` when duplicated)
- [ ] Duplicate ids: use structured `diagnostic.occurrences`; do not auto-rename
- [ ] A11y IDREF findings may include review-only similar-id candidates — candidate ≠ fix; never invent ids
- [ ] `role="tab"` includes `aria-controls` → tabpanel
- [ ] Explicit light theme uses `data-velin-theme="light"` (and/or `data-velin-color-scheme="light"`) so `prefers-color-scheme: dark` cannot override
- [ ] No positive tabindex; no nested interactive / interactive-in-`<summary>`
- [ ] Empty links / missing accessible names (`a11y/empty-link`) — use structured `diagnostic` name-source flags; do not invent names from URLs
- [ ] Button / input labels — read `diagnostic` name/label sources; `forVerified` only when `label[for]` uniquely matches; never invent labels/ids/aria-*
- [ ] Nested interactive — use `diagnostic.outer` / `inner`; structure choice is developer review; never auto-remove/rewrite controls
- [ ] `role="button"` contract — `missing-tabindex` vs `keyboard-handler-not-detected`; `runtimeUnverified` (no module-JS detection); never auto-add tabindex/handlers
- [ ] Heading order — use real skip line + `previous`/`current` levels; never auto-rewrite heading hierarchy
- [ ] Scrollable `<pre>` — structured overflow flags; staticOnly / runtimeUnverified; never auto-add tabindex or change overflow CSS
- [ ] Positive tabindex — review `diagnostic.numericValue` / other positives; do not auto-set 0/−1
- [ ] `:focus { outline: none }` without `:focus-visible` compensation — static CSS diagnostic only, not runtime focus proof
- [ ] Light/dark chrome: accent text on tinted surfaces still meets AA (token/CSS review; not auto-fix)
- [ ] Narrow viewports (≈320 CSS px) reflow; treat browser `zoom` overflow as review, not auto-CSS
- [ ] Optional: `velinstyle scan --only links` for static local href / fragment integrity (`links/local-href-target`, `links/local-fragment-target`) — Detect only; never auto-rewrite paths
- [ ] Link findings include structured `diagnostic` (href, resolved path, reason, candidates, confidence) — machine-readable; candidate ≠ fix; `exact` confidence does not enable Apply
- [ ] Safe attribute fixes (`html-lang`, `skip-link` with `#main`, etc.): use `--fix-dry-run` / `autoFix.plans` before `--fix`; never Apply from Candidates alone; no finding `action` field
- [ ] Reduced-motion note (BP-A02)
- [ ] No Write

## Best practices
- BP-V01, BP-A01 (when a11y), BP-T02 (when theme)

## Anti-patterns
- Silent WRITE/APPLY
- Treating review success as license to auto-map Tailwind residuals

## Inputs / Outputs
- Inputs: html, docs
- Outputs: checklist, report
