Skip to content

test(frontend): assert the two WCAG 2.2 criteria that were satisfied but unguarded - #80

Merged
khafifithebork merged 2 commits into
masterfrom
feat/wcag-22-guards
Sep 27, 2026
Merged

khafifithebork merged 2 commits into
masterfrom
feat/wcag-22-guards

Conversation

@khafifithebork

Copy link
Copy Markdown
Owner

Roadmap C3 and two of C4's three tests — the first work to follow from ADR-030 being accepted. No behaviour changes. These pin properties the audit found already true, which by ADR-023 §1 means they weren't yet controls.

3.3.8 Accessible Authentication

A password manager must be able to fill these forms, and autocomplete is how a field says what it is.

The regression isn't hypothetical. Stripping autocomplete from a login form is a folk security measure — it looks like hardening, and it breaks the exact mechanism this criterion relies on. Nothing in this repository would have noticed.

Asserted across all four auth pages on the values, not mere presence: a manager offers a saved password for current-password and a generated one for new-password, and swapping those two is the failure that looks like it works. Rendered rather than grepped, because Field is what actually puts the attribute on the input — a source check would pass if it stopped forwarding the prop.

Also asserts no CAPTCHA, the other half of the criterion. Bot traffic is already handled by django-axes lockout and per-IP throttling rather than by asking a user to read distorted letters.

2.5.8 Target Size

24×24 is the floor. Asserted against the variant strings, not a rendered box — jsdom computes no layout, so getBoundingClientRect returns zeroes and a rendered assertion there would be measuring nothing.

Button had no test file at all; it now also covers aria-busy on the pending state, the other accessibility-relevant thing it does. One assertion is about a bug rather than a criterion: the base's border border-transparent keeps a bordered variant the same height as an unbordered one beside it. A minimum height doesn't fix that — a minimum is a floor, not a cap, measured when the landing page's two CTAs came out 44px and 46px.

C3 is one line, and the useful part is what didn't change

contrast.test.ts now cites 2.2. Its arithmetic is untouched, because 2.2 changes no contrast threshold — the target moved and this file needed nothing, which is worth recording rather than leaving someone to wonder whether it was missed.

Provocations

Provocation Fails
autoComplete stripped from the password field "login declares autocomplete on every input"
current-password swapped for new-password the same test
sm loses its minimum height "sm declares a minimum height of at least 24px"
the transparent border removed "keeps the same minimum whether or not the variant draws a border"

Two of my own slips, both caught by running things

  • getAllByRole("textbox") throws rather than returning an empty list, and reset-password has no textbox — only a password field.
  • querySelectorAll is typed Element, which doesn't satisfy concat on an HTMLElement[]. The tests passed while tsc did not — a reminder that a green suite isn't a green build.

What still waits

The third C4 test waits on A2. There is no 2.4.11 remedy to test until somebody reproduces the fault, and ADR-006 forbids fixing what hasn't been seen.

Section A of the roadmap is untouched and still needs a person — starting with the reduced-motion check, outstanding since 2026-09-25.

438 frontend tests across 39 files, tsc, eslint, verify:css, verify:a11y, verify:static all pass.

🤖 Generated with Claude Code

khafifithebork and others added 2 commits September 27, 2026 15:29
…four accepted

The owner declined the manual theme toggle (ADR-032 §3) and deferred localisation
(ADR-033 §4) on 2026-09-27. With ADR-030 and ADR-031 already accepted, all four
ADRs from the frontend audit are now decided and **nothing in the roadmap waits on
a decision.**

Both ADRs record what acceptance does *not* mean, because that is where a document
starts overstating itself:

- **ADR-032** settles the theme mechanism — one media query, semantic tokens, no
  stored preference, and a test that fails if a `data-theme` selector appears. It
  does not close roadmap A4: nobody has looked at a dark-mode scrollbar or an
  autofilled field. And declining the toggle is not forbidding one — a real user
  request supersedes the ADR, and §3 records the cost so that conversation starts
  from a number.
- **ADR-033** defers the interface language. It does **not** license writing
  English-only assumptions into the code, which is the whole point of §2's habit,
  and it does not close §3: `<track>` still lacks `srclang` and `label`. That is a
  defect rather than a decision and it is blocked on backend modelling, because the
  correct value is not knowable from the frontend. Deferring the interface language
  and failing to say what language the captions are in are different problems and
  only one of them is settled.

**The practice worth keeping is recorded in STATUS.** Both of these ADRs were split
before being decided, each having mixed "this is broken" with "what should we do" —
and the broken half never needed the decision. `color-scheme` and the logical
properties shipped hours ahead of the questions they had been sitting behind. When
an ADR contains both, split it.

The defect table is rewritten by state rather than by count: two fixed, one open,
one blocked, plus a fifth found while fixing the fourth. It also keeps the
corrections — measurement disproved part of ADR-032 §2, because Tailwind's Preflight
already resets form controls; and ADR-033's "about four" utilities were nine.

Docs only. 425 frontend tests still pass, unchanged. Every ADR id and doc path
resolves, and no document still describes any of the four as proposed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…but unguarded

Roadmap C3 and two of C4's three tests, the first work to follow from ADR-030 being
accepted. No behaviour changes — these pin properties the audit found already true,
which by ADR-023 §1 means they were not yet controls.

**3.3.8 Accessible Authentication.** A password manager must be able to fill these
forms, and `autocomplete` is how a field says what it is. The regression is not
hypothetical: stripping `autocomplete` from a login form is a folk security
measure, it looks like hardening, and it breaks the exact mechanism the criterion
relies on. Nothing here would have noticed.

Asserted across all four auth pages, on the *values* rather than mere presence —
a manager offers a saved password for `current-password` and a generated one for
`new-password`, and swapping those two is the failure that looks like it works.
Rendered rather than grepped, because `Field` is what actually puts the attribute
on the input and a source check would pass if it stopped forwarding the prop. Also
asserts no CAPTCHA: that is the other half of the criterion, and bot traffic is
already handled by `django-axes` lockout and per-IP throttling rather than by
asking a user to read distorted letters.

**2.5.8 Target Size.** 24×24 is the floor. Asserted against the variant strings,
not a rendered box — jsdom computes no layout, so `getBoundingClientRect` returns
zeroes and a rendered assertion would be measuring nothing. `Button` had no test
file at all; it now also covers `aria-busy` on the pending state, which is the
other accessibility-relevant thing it does.

One assertion there is about a bug rather than a criterion: the base's
`border border-transparent` keeps a bordered variant the same height as an
unbordered one beside it. A minimum height does not fix that, because a minimum is
a floor and not a cap — measured when the landing page's two calls to action came
out 44px and 46px.

**C3** is one line: `contrast.test.ts` now cites 2.2. Its arithmetic is untouched
because 2.2 changes no contrast threshold, which is the useful thing to record — the
target moved and this file needed nothing.

Four provocations, all failing the right test: stripping `autoComplete`, swapping
`current-password` for `new-password`, removing `sm`'s minimum height, removing the
transparent border.

Two of my own slips, both caught by running things: `getAllByRole("textbox")` throws
rather than returning an empty list, and reset-password has no textbox — only a
password field. And `querySelectorAll` is `Element`, which does not satisfy `concat`
on an `HTMLElement[]`; the tests passed while `tsc` did not.

**The third C4 test waits on A2.** There is no 2.4.11 remedy to test until somebody
reproduces the fault, and ADR-006 forbids fixing what has not been seen.

438 frontend tests across 39 files, tsc, eslint, verify:css, verify:a11y and
verify:static all pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@khafifithebork
khafifithebork merged commit b07e952 into master Sep 27, 2026
8 checks passed
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