Skip to content

test(console): an accessibility gate — and the two real defects it found immediately - #79

Merged
omercelikdev merged 2 commits into
mainfrom
test/console-a11y
Jul 27, 2026
Merged

test(console): an accessibility gate — and the two real defects it found immediately#79
omercelikdev merged 2 commits into
mainfrom
test/console-a11y

Conversation

@omercelikdev

Copy link
Copy Markdown
Owner

An operations console is used at 3am, over a remote session, by whoever is on call. Being operable by keyboard on an imperfect screen is a correctness property, not polish — so the smoke now runs axe (WCAG 2.1 A/AA, serious + critical only) over every panel and over the confirm dialog, where destructive verbs are decided.

The two defects its first run found

1. The secondary inks failed the contrast threshold.
--faint was 2.56:1 on white; --muted-foreground 4.44:1 on the surface. That is most of the console's supporting text — ages, counts, hashes, timestamps — below AA on a normal screen, never mind a glare-lit one. Both re-picked against the surface (the darker of the two backgrounds they sit on), with the measured ratios written beside them. The dark theme's faint (3.70:1) too.

2. Escape did not close the confirm dialog.
An operator who opened erase by mistake had to go find the mouse. Escape now cancels — and forgets the note typed into it, so a half-written subject key cannot ride along on the next attempt.

Focus moves to the dialog when it opens — once, deliberately: my first attempt re-focused on every render and yanked the caret out of the note field between keystrokes. The kit's own tests caught that before it left the branch.

The page also declares its language (html-has-lang).

Scope of the gate

Serious and critical only. The "moderate" ramp (landmark preferences, heading order in a dense panel) is advice, and a gate that fails on advice gets muted — which would cost the real findings too.

Console smoke 15/15 (9 behaviour + 6 accessibility). kit 64 tests, console 101.

docs/strategy/ui-standard-v1.md now carries the corrected tokens and says the gate is what corrected them.

🤖 Generated with Claude Code

…und immediately

An operations console is used at 3am, over a remote session, by whoever is on call: being
operable by keyboard on an imperfect screen is a correctness property, not polish. The
smoke now runs axe (WCAG 2.1 A/AA, serious + critical only) over every panel and over the
confirm dialog, where destructive verbs are decided.

Its first run found two real defects, both now fixed:

1. The secondary inks failed the contrast threshold. `--faint` was 2.56:1 on white and
   `--muted-foreground` 4.44:1 on the surface — so a large share of the console's
   supporting text (ages, counts, hashes, timestamps) was below AA on a normal screen.
   Both are re-picked against the SURFACE, the darker of the two backgrounds they sit on,
   with the measured ratios written next to them; the dark theme's faint (3.70:1) too.

2. ESCAPE did not close the confirm dialog. An operator who opened `erase` by mistake had
   to find the mouse. Escape now cancels — and forgets the note typed into it, so a
   half-written subject key cannot ride along on the next attempt.

Focus moves to the dialog when it opens (once, deliberately: my first attempt re-focused
on every render and yanked the caret out of the note field between keystrokes — caught by
the kit's own tests before it left the branch).

The page also declares its language.

Console smoke: 15/15. kit 64, console 101.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@omercelikdev

Copy link
Copy Markdown
Owner Author

Review agent v1 — 2 finding(s). The human decides; hard-stop labels need explicit resolution.

  • R3 (medium) ui/kit/src/components/VerbButton.tsx:84 — Escape (and the cancel button) unmount the confirm <span> while it still holds DOM focus, so the browser drops focus to instead of returning it to the button that opened the dialog — the keyboard operator who just backed out loses their place on the page.
    • evidence: cancel (line 50-53) and the Escape handler (line 84-89) never restore focus to the trigger button; the new a11y gate only checks axe violations and dialog count after Escape (a11y.spec.ts:54-55), not document.activeElement, so this regression survives the gate the PR credits with finding the other two defects. · action: Store a ref to the button that opened the confirm and call .focus() on it inside cancel, so keyboard focus lands back where the operator was instead of resetting to the top of the page.
  • R5 (medium) ui/console/e2e/a11y.spec.ts:41 — The test titled "a confirm dialog traps nothing and is reachable by keyboard alone" opens the dialog with .click() (line 45) — keyboard-only reachability is never exercised, and nothing asserts a focus trap (tab-cycling) either, so the title claims more than the test checks.
    • evidence: line 45 uses .getByRole(...).first().click(); the only keyboard interaction in the test is the Escape press on line 54, which backs the dismissal half of the title but not "reachable by keyboard alone" or "traps nothing". · action: Either drive the trigger via keyboard (Tab/Enter) and add a tab-cycling assertion to back the full title, or narrow the title to what's actually verified (axe-clean + Escape-dismissible).

Calibration: mark each finding accepted/dismissed in a reply — dismiss rate >40%/class revises that class (strategy §5).

Escape unmounted the dialog while it still held focus, so the browser dropped focus to
<body> — a keyboard operator who backed out of a destructive confirm lost their place on
the page. Focus now returns to the button that opened it.

The a11y test's title also claimed more than it checked: it opened the dialog with a
click. It now opens from the keyboard, dismisses from the keyboard, and asserts the
hand-back — and the title says only what the test proves (the dialog is inline, not a
modal overlay, so there is no focus trap to assert, and the comment says so).

15/15.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@omercelikdev

Copy link
Copy Markdown
Owner Author

Both accepted.

R3 — Escape unmounted the dialog while it held focus, so focus fell to <body>: the operator who backed out of a destructive confirm lost their place. Focus now returns to the trigger, asserted in the kit test and in the browser.

R5 — the title claimed more than the test checked. It now opens the dialog from the keyboard, dismisses it from the keyboard, and asserts the hand-back; the title says only that. There is no focus trap to assert — the confirm is inline, not a modal overlay — and the test now says so in a comment rather than implying otherwise in its name.

@omercelikdev
omercelikdev merged commit 15ec047 into main Jul 27, 2026
3 checks passed
@omercelikdev
omercelikdev deleted the test/console-a11y branch July 27, 2026 11:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant