accessibility/
Accessibility Is a Delivery Gate, Not a Cleanup Week

Andrei Bespamiatnov
Author
Cleanup week is where a11y goes to die
I have been in the room where accessibility is scheduled after feature complete. The deck looks responsible. The queue looks infinite. Contrast bugs lose to a launch date, and keyboard users get the backlog.
The teams that actually ship accessible UI treat WCAG like they treat a red unit test: it can fail the build, and someone owns the red.
That shift is not cultural poetry. It is fixtures, commands, and a gate.
Put the rule next to the flow, not in a wiki
A wiki page named “WCAG checklist” will be stale before the next sprint.
What works:
- A small set of rules the pipeline runs on every PR that touches UI.
- The same rules in the browser you already use for E2E (for us that has been Cypress plus axe).
- Failures attached to a flow: “submit answer,” “open settings,” “finish session” — not “the app in general.”
“The app in general” is how you get a 400-violation dump that nobody triages. A flow is something a developer can open and fix.
Custom commands beat one-off cy.injectAxe()
If every spec invents its own scan, you will get five definitions of “critical.”
I keep a short command layer:
- Visit a page (or a state) the way a user does.
- Scan with a documented tag set (wcag2a / wcag2aa, and only the rules you have agreed to enforce).
- Assert with a message that names the flow, not the library.
That command is the product. Specs stay thin. When we raise the bar — more tags, fewer exceptions — we change one place.
Exceptions exist. A third-party canvas or a licensed grid will fail rules you cannot fix this month. Put those exceptions in a list with an owner and a review date. An unlabeled cy.configureAxe({ rules: { ... disable everything } }) is not an exception. It is surrender.
Contrast is a token problem
Most “we failed color contrast” reports are not a missing aria-label. They are a theme token that looked fine on a designer’s calibrated laptop.
If you have a dark theme and a light theme, scan both. Hosted UI (a panel inside another product) must scan against the host theme, not only your default.
I treat token files as the fix site. Patching one button to a magic hex because axe yelled is how you get a second violation on the next screen that used the same token.
Keyboard is a journey, not a tab index contest
Axe will not feel a trap. A person will.
For flows that matter, I still write a few explicit keyboard journeys: open the dialog, move through actions, close, land somewhere predictable. If the focus ring is outline: none without a replacement, that is a defect, not a style choice.
Visual parity work — making a new UI match a trusted old one — is where this bites. The old UI had a focus treatment you stopped seeing. The new one dropped it. Users who never used a mouse notice immediately.
Make the gate boring
A good gate has three properties:
- It is fast enough that people do not skip it locally.
- It fails with a URL and a rule id, not a screenshot of a wall of JSON.
- It is owned. A red a11y job is someone’s rotation, like a red unit test job.
I do not run the entire WCAG catalog on every commit on day one. I turn on a slice, get to green, then widen. A gate that is always red is not a gate. It is wallpaper.
What this is not
This is not a claim that automation replaces people with assistive technology. It does not. Screen-reader passes and cognitive reviews still need humans.
Automation’s job is to stop the boring regressions — contrast, missing names, broken labels — from eating the human session. If the machine cannot fail those, the human session becomes a scavenger hunt.
A starting kit
- Pick one user flow you already test in Cypress.
- Add one scan command with a tight rule set.
- Run it in CI on that flow only.
- Fix the tokens it finds.
- Add the next flow when the first stays green for a month.
After a quarter you will have a practice. After a cleanup week you will have a slide.