Skip to content

add(a11y): Focus not obscured (WCAG 2.2 SC 2.4.11 + 2.4.12)#113

Merged
jdevalk merged 1 commit into
mainfrom
standards-scan/focus-not-obscured-2026-07-17
Jul 17, 2026
Merged

add(a11y): Focus not obscured (WCAG 2.2 SC 2.4.11 + 2.4.12)#113
jdevalk merged 1 commit into
mainfrom
standards-scan/focus-not-obscured-2026-07-17

Conversation

@jdevalk

@jdevalk jdevalk commented Jul 17, 2026

Copy link
Copy Markdown
Owner

What changed

New page: Focus not obscured (accessibility, recommended, order 55), covering WCAG 2.2 SC 2.4.11 Focus Not Obscured (Minimum) (Level AA) and SC 2.4.12 (Enhanced) (Level AAA).

Plus: focus-indicators hands the topic off, reciprocal relatedSlugs on four pages, changelog entry, OG images, sign:skill 163 → 164.

Why now

This is the last WCAG 2.2 gap. Cross-checking all nine new 2.2 success criteria against the full corpus:

SC Level Before this PR
2.4.11 Focus Not Obscured (Min) AA one sentence on focus-indicators
2.4.12 Focus Not Obscured (Enh) AAA absent from src/ entirely
2.5.7, 2.5.8, 3.2.6, 3.3.7, 3.3.8, 3.3.9 covered

It follows the one-page-per-SC pattern established by #91 (dragging-movements) and #92 (consistent-help), and pairs the AA and AAA variants on a single page exactly as accessible-authentication already pairs 3.3.8 with 3.3.9.

Why a separate page, not a paragraph on focus-indicators

They fail independently, and the fixes live in different places.

2.4.7 asks whether you drew a focus indicator. 2.4.11 asks whether anything is sitting on top of the one you drew. A site can ship an immaculate three-pixel high-contrast ring and still fail 2.4.11 the moment Tab scrolls a link under a 64px sticky header. The ring is rendering perfectly — it is simply underneath something. The remedy is scroll-padding-top on the scroll container, not a focus style. That's why WCAG 2.2 made it a separate criterion rather than a note on 2.4.7, and it's why it earns a separate page here.

focus-indicators previously compressed this into one sentence plus a scroll-margin bullet that was actually the wrong fix (scroll-margin is per-target and handles anchor jumps; it does nothing for Tab). It now hands the topic off.

Scope rule check — auditable outcome

Per the "auditable website outcome, not build technique" rule: tab through the page and watch whether the focused control disappears behind a sticky element. Externally checkable, no source access required, and the "Why it matters" lands squarely on visitors — sighted keyboard users, switch-access and voice-control users — not on the developer.

Worth noting why this bug survives: screen reader users are unaffected (content is announced from under the banner just fine), and no automated checker catches it. It only surfaces when a sighted person tabs through and watches. That's the page's reason to exist.

Status justification

recommended, not required. 2.4.11 is Level AA, but the web platform contract does not break without it — per CLAUDE.md the bar for required is "the platform breaks", and the default is recommended. This matches dragging-movements (2.5.7, AA) and consistent-help (3.2.6, AA), both AA and both recommended.

Sources

  • Understanding SC 2.4.11 — normative text verified verbatim: "the component is not entirely hidden due to author-created content".
  • Understanding SC 2.4.12"no part of the component is hidden by author-created content".
  • WCAG 2.2 Recommendation
  • MDN scroll-padding — canonical path resolved via the MDN MCP server per CONTRIBUTING, not hand-written.

Both exceptions (user-movable content, user-opened content) are taken from the normative notes rather than paraphrased from memory.

One drive-by correction, and one thing I did not do

Added a 1.4.11 Non-text Contrast citation to focus-indicators. Its existing "3:1 against the adjacent colour … (1.4.11)" line had no source behind it. That line is correct — I checked, because it initially looked like a misattribution of 2.4.13's requirement. It isn't: 1.4.11 genuinely is the adjacent-contrast rule.

Left alone, flagged for follow-up: 2.4.13's own contrast test — 3:1 between the same pixels in the focused and unfocused states — is stated nowhere on the site. focus-indicators has 2.4.13's 2px-area half but not its contrast half, and W3C explicitly distinguishes the two ("this differs from Non-text Contrast criteria, which measure adjacent contrast"). That's a real omission but a separate topic, so it isn't in this PR.

Verification

  • npm run build passes — 164 pages indexed.
  • Page confirmed live on /llms.txt, /checklist/, /spec/accessibility/, sitemap-accessibility.xml, the .md endpoint, and the /okf/ bundle.
  • npm run assets run; staged the new OG image plus the count-driven og-default.png, og/checklist.png, og/spec.png, og/spec/accessibility.png. All other images byte-identical, as the deterministic generator promises.
  • npm run sign:skill run — 163 → 164 pages, digest recomputed; pre-commit drift check passes.
  • format:check clean.

Note: npm run assets also re-vendored public/vendor/purify.min.js, which I deliberately excluded from this commit — dompurify was bumped to 3.4.12 in #100 but the vendored copy was never regenerated, so it's drifted. That's a real (if minor) finding, but a vendored security asset shouldn't ride along in a spec-page PR. Flagging separately.

🤖 Generated with Claude Code

The last WCAG 2.2 gap. Cross-checking all nine new 2.2 success criteria against
the corpus: 2.5.7, 2.5.8, 3.2.6, 3.3.7, 3.3.8 and 3.3.9 all have pages; 2.4.11
had one sentence on focus-indicators; 2.4.12 appeared nowhere in src/.

Follows the one-page-per-SC pattern of #91 (dragging-movements) and #92
(consistent-help), and pairs the AA and AAA variants on one page the way
accessible-authentication already pairs 3.3.8 with 3.3.9.

Why it is a separate page rather than a paragraph on focus-indicators: they
fail independently and the fixes live in different places. 2.4.7 asks whether
you drew a focus indicator. 2.4.11 asks whether anything is sitting on top of
the one you drew. A site can ship an immaculate ring and still fail the moment
Tab scrolls a control under a sticky header — the ring renders perfectly, it is
just underneath something, and the remedy is scroll-padding on the scroll
container, not a focus style.

Auditable outcome, per the scope rule: tab through the page and watch whether
the focused control disappears. Externally checkable, no source access needed.

Status `recommended`, not `required`: 2.4.11 is Level AA, but the platform
contract does not break without it. Matches dragging-movements and
consistent-help, both AA and both recommended.

focus-indicators now hands the topic off instead of half-covering it, and drops
its 2.4.11 citation (the new page owns it). Its 1.4.11 citation is added, which
is what the existing "3:1 against the adjacent colour" line was already
asserting without a source behind it.

Wired both ways into focus-indicators, keyboard-navigation, inert-attribute and
cookie-consent. Changelog entry, OG images (new page + the 3 count-driven ones),
and sign:skill 163 -> 164 pages included.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying specification-website with  Cloudflare Pages  Cloudflare Pages

Latest commit: 33dbf32
Status: ✅  Deploy successful!
Preview URL: https://653b5906.specification-website.pages.dev
Branch Preview URL: https://standards-scan-focus-not-obs.specification-website.pages.dev

View logs

@jdevalk
jdevalk marked this pull request as ready for review July 17, 2026 06:43
@jdevalk
jdevalk merged commit 726620a into main Jul 17, 2026
8 checks passed
@jdevalk
jdevalk deleted the standards-scan/focus-not-obscured-2026-07-17 branch July 17, 2026 06:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant