Every code block on the public API docs is a scroll region a keyboard cannot reach — 20+ panes of the reference are readable by mouse only
Surfaced by MOTIR-2482 — the first sweep ever to load /docs with axe. Filed rather than absorbed, per that card's acceptance criterion: "any defect it surfaces that is NOT a --el-text-* contrast failure logged as its own bug rather than absorbed here."
Repo: motir-core. One PR.
The measurement
tests/e2e/shell-a11y-wide.spec.ts sweeps /docs (which redirects to /docs/api) with WCAG 2.1 A+AA and zero rule exclusions. axe reports scrollable-region-focusable (serious) on 20+ nodes — one per request/response sample in the reference:
#listProjectWorkItems > … > pre
#createWorkItem > … > pre
#getWorkItem > … > pre
#updateWorkItem > … > pre
#transitionWorkItem > … > pre
… (and the rest of the operation set)
The cause
The code panes render as <pre> inside a .relative.overflow-hidden.rounded-(--radius-card) wrapper. The pane scrolls (a request body is wider and taller than its box) but nothing in it is focusable, so there is no way to scroll it from the keyboard: no tabindex, no focusable child, no scrollbar a keyboard reaches. A sighted mouse user drags it; a keyboard-only user sees the first few lines of every sample and cannot reach the rest.
This is the DEVELOPER documentation — the surface whose whole job is to be read, and the one page in the product deliberately published without a session gate so a prospective integrator can read it before signing up.
Scope
In: making the code panes keyboard-scrollable, in CodeBlock / DocBlocks (one shared fix, not 20 call sites), and whatever accessible name that requires so the focus stop announces as something.
Out: the docs catalogue's eyebrow contrast — already fixed under MOTIR-2482, which owned the --el-text-* arm.
Acceptance criteria
/docs/apireports ZEROscrollable-region-focusableviolations under the sameWCAG_TAGSand zero rule exclusions the other sweeps use, with the reference fully rendered.- The fix lands in the shared code-block component, so a new operation added to the reference inherits it — verified by the sweep covering the whole page rather than a spot check.
- A focusable scroll region has an accessible NAME (a
role="region"+aria-label, or a labelledtabindex="0"container) — a baretabindex="0"clears the axe rule while announcing as "group, blank", which trades a violation for a worse experience. - A pane that does NOT overflow does not become a focus stop — 20+ empty tab stops on one page is its own defect. If that cannot be determined statically, say so in a comment and take the simpler branch deliberately.
- The
disableRulesentry this defect forced intotests/e2e/shell-a11y-wide.spec.ts's/docssweep — commented with this card's key — is REMOVED in the same PR, so the route returns to a zero-exclusion sweep. pnpm lint,pnpm typecheck,pnpm prettierandnext buildpass.
Context refs
app/(public)/docs/_components/CodeBlock.tsx— the pane.app/(public)/docs/_components/DocBlocks.tsx— where the reference composes them.tests/e2e/shell-a11y-wide.spec.ts— the sweep that measured it, and the carve-out to remove.app/(public)/docs/layout.tsx— the public, session-less framing of this surface.