All writing

Accessibility is an engineering practice, not an audit

Accessibility usually fails not because a team doesn't care, but because of when they decide to. Build the whole product, book an audit near the end, then triage a long list of findings against a ship date that's already slipping. By then the problems are baked into components used in fifty places. The people who wrote them have moved on. And accessibility turns into a tax paid under duress instead of a property of the work. I've come around to thinking the fix has to be structural. Stop treating it as a phase. Start treating it as engineering, something enforced on every change, by machines, all the time.

An audit is a snapshot; a gate is a ratchet

An audit tells you how the product looked on the day someone looked. A photograph. The next pull request merges and that photo is already out of date, and the result you paid for starts to rot. A CI gate works differently. It's a ratchet. Once a check is in, the build refuses to go backward. A new color pairing that drops below contrast, a button that's really a div with no role, an image shipped without alt text. These stop being things you hope a reviewer notices and become things that fail the build, in minutes, on the branch, before anyone has merged. On this site the ratchet is real: a Lighthouse accessibility budget that has to hit a perfect 1.0 or the pipeline fails, and a Playwright suite that asserts zero accessibility violations on every route, in English and Spanish, in light and dark. No change can quietly walk any of that back, because the build won't let it. I'm not trying to replace human judgment. I'm trying to make the machine-checkable failures impossible to ship by accident, so people can spend their attention on the parts only people can judge.

Where the checks live

No single tool catches everything, so I layer them. Each one runs on its own, and each one blocks a merge when it fails:

  • axe inside the unit tests. Components get checked against accessibility rules at the smallest unit, so a violation shows up next to the code that caused it, not three integrations later.
  • axe inside Playwright. The same rules run against fully rendered pages in a real browser, on every route in both languages and both themes, where dynamic state, focus order, and content that only exists after you interact are actually there to be checked.
  • Lighthouse. A budget on the overall accessibility score that fails the pipeline if it slips below a perfect 1.0, catching whole-page problems the component tests can't see.

What these tools have in common is honesty about their own limits. Automated checks reliably catch a real share of accessibility problems, like contrast, missing names, broken roles, and malformed structure, but they can't tell you whether your alt text actually means anything, or whether someone using only a keyboard can finish the task. That's the whole reason I want machines handling the machine-checkable failures. It frees up the manual and assistive-technology testing for the judgment calls no linter will ever make.

Two different bars, on purpose

Be precise about which standard you're holding yourself to, because the honest answer changes with the stakes. WCAG, the Web Content Accessibility Guidelines, now version 2.2, which the W3C published as a Recommendation in 2023, defines three conformance levels: A, AA, and AAA. For public systems, AA is the legal and practical floor. It's the level Section 508 and the ADA effectively point at, and it's the right baseline for a government service that has to serve everyone. AAA is the strictest level, and the guidelines themselves note it isn't reachable for all content. Which is exactly what makes it an interesting target for a small surface you fully control.

So I hold two different bars on purpose, and I keep them apart. MyCareer.NJ, a statewide platform serving roughly 1.7 million residents, is held to the AA floor that public services require, and the engineering record backs it up: a 100% Lighthouse accessibility score, and 100% "Good" Core Web Vitals across nearly 200,000 URLs, with bilingual English/Spanish parity treated as part of accessibility, not a separate feature. That's the right, defensible standard for a government system at that scale.

This site is a different animal. It's small, it's entirely mine, and nobody's livelihood rides on it, which makes it the perfect place to hold the highest bar with no compromise. So I built it to WCAG 2.2 AAA: the strictest contrast ratios, no reliance on color alone, fully operable by keyboard, structured so assistive technology can move through it cleanly. I'm not claiming AAA is the right target for every project. It isn't, and pretending otherwise would be the kind of imprecision this whole post argues against. I claim it as proof of practice. Building this site to AAA is how I show that the gates I'm describing are real, that I run them on my own work, and that I'd rather demonstrate the discipline than just write about it.

Why the gate changes the culture

The deepest reason I prefer gates to audits has nothing to do with tooling. It's what each one teaches a team. An end-of-project audit teaches engineers that accessibility is somebody else's job, checked later, by someone with a clipboard. A gate on every pull request teaches the opposite. Accessibility is your job, right now, the same as making the tests pass. When the feedback lands in minutes instead of months, people learn the rules by writing inside them, and the next component starts out accessible because that's just how the team builds. Accessibility stops being something you remediate and becomes something you have.

An audit asks, at the end, whether you got there. A practice makes sure you never left. For software that public institutions and the people who depend on them actually use, that difference isn't pedantry — it's the whole thing.