← All posts
Concepts

Accessible semantics deserve regression tests

Accessibility defects can survive a visual review: an unlabeled field, a control with no keyboard equivalent, or a modal with broken focus management. Treat these as behavior to check continuously, rather than work to postpone until an audit.

What to test, and what still needs an audit

WCAG name, role and value guidance describes programmatically identifiable control semantics. Keyboard operability is another requirement. These checks are useful engineering targets, but passing them alone does not establish WCAG conformance or compliance with an applicable law. Legal scope, exemptions, transitions and enforcement differ; confirm those separately with the relevant official authority.

Strip WCAG down to what it means for the DOM your app ships, and a large share of it comes down to one thing: every meaningful control has to announce what it is and what it's called. A button has to be a button, with a name. A form field has to have an associated label. An image that carries meaning needs a text alternative. A heading has to be a heading, not styled text pretending to be one. These are the accessible semantics, the roles and names and labels that assistive technology reads out loud so a screen reader user can operate your app without seeing it.

Most teams miss the part that makes this an engineering problem and not just a compliance one: those same semantics are what a good end-to-end test suite depends on.

Be honest about what testing does and doesn't do

Let me draw the line clearly, because it matters and because it's easy to oversell.

TestVibe is not an accessibility auditor. It does not produce a WCAG conformance report. It won't scan your color contrast ratios, tell you your compliance percentage, or certify you against the EAA. Conformance is a dedicated discipline. You need real audits, automated scanners like axe, and, crucially, testing with actual assistive technology and disabled users. Nothing on this page replaces that. If someone sells you "one-click EAA compliance," close the tab.

What functional end-to-end tests do is create standing pressure on the accessible semantics you already have. That's a narrower claim, but it's a true one, and it turns out to be worth a lot.

Accessible semantics are the locators

The most durable way to find an element in a browser test is by its accessible role and name, the same handle a screen reader uses. In Playwright terms, getByRole('button', { name: 'Place order' }) and getByLabel('Email address') don't reach into your CSS classes or your DOM nesting. They reach for the thing a human, sighted or not, would use to identify the control.

// Anchored to accessible semantics, not markup structure
await page.getByRole('button', { name: 'Place order' }).click();
await page.getByLabel('Email address').fill(email);
await expect(page.getByRole('alert')).toHaveText('Order confirmed');

This is already the resilient locator strategy, the one that survives a CSS refactor, a component-library swap, or the DOM nesting one level deeper. We've argued before that role-and-name locators are the most robust way to write a test that doesn't break on cosmetic churn.

But look at what the locator requires to work. getByRole('button', { name: 'Place order' }) can only find that button if the button is a real button (or has role="button") and exposes the accessible name "Place order." The day a developer replaces it with a clickable <div>, drops the label, or nukes the aria-label in a refactor, the locator returns nothing and the test fails.

That failure is the whole point. A control that loses its accessible name breaks the locator that was pointing at it, so the accessibility regression surfaces as an ordinary red test on the pull request that caused it. You don't need a quarterly audit to find out that a control lost its name. Your suite tells you the same day the change lands, in CI.

Accessibility debt and testability debt are the same debt

What makes an app testable and what makes it accessible are mostly the same properties. An app that's hard to write stable tests against (where you're forced into nth-child selectors and brittle class chains because nothing has a stable, meaningful handle) may expose semantics that also need accessibility inspection, for exactly the same reason: there's no semantic surface to grab onto. An app where every control has a clear role and a stable accessible name is easier to test, but keyboard navigation and assistive-technology behavior still require their own checks.

So the two debts move together. Give a control a proper role and name to make it testable, and you've made it more accessible at the same time; fix a missing label for accessibility, and you've made a more stable locator possible. That's rare in engineering, because compliance work and quality work usually compete for the same budget. Here they're the same work.

This is also why accessibility fits naturally into a shift-left quality posture. You catch semantic regressions continuously, on every change, as a byproduct of tests you were going to write anyway, instead of scrambling to fix them all in the week before a launch audit. Repairing a broken label on the pull request that introduced it costs a fraction of what it costs to find it three months later.

Keyboard flows are just test scenarios

There's a second WCAG pillar that translates cleanly into tests: operability without a mouse. WCAG keyboard guidance requires equivalent keyboard operability, with an exception for functionality that depends on the path of movement. For an ordinary form journey, test the keyboard path: tab to the field, type, tab to the button, press Enter. For a lot of teams this is the most quietly broken part of their UI, because nobody on the team drives the app by keyboard, so nobody notices when a modal traps focus or a custom dropdown eats the arrow keys.

You can turn that into a regression test by describing it in plain language, the same way you'd describe any other flow. "Open the login form, tab to the email field, type the address, tab to password, type it, press Enter, and confirm the dashboard loads." When you write that as a test description, you get a generated Playwright scenario that exercises the keyboard path and asserts on the outcome with a web-first assertion that retries until the dashboard is actually there. Break focus management later, and the scenario goes red.

Again, this is not a WCAG audit. It's a targeted regression check on the specific keyboard journeys you decided matter. Prioritize essential user journeys: the checkout that can't be completed without a mouse, the sign-up a screen reader user can't finish. Keep those journeys in regression coverage, and rerun them after changes.

The honest bottom line

If you have EU customers, you need a proper accessibility program — audits, scanners, assistive-technology testing, ideally disabled users in the loop. Nothing here changes that. Treat any tool that claims to be your compliance program as a red flag.

Functional tests can catch regressions in the roles, names, labels and keyboard paths they actually exercise. Untested controls and behaviors can still regress, and a role-based test does not replace assistive-technology testing. The cheapest layer of defense here is one you were probably building for stability reasons anyway; point it at your semantics and it pulls double duty for compliance too.

Want tests that anchor to the accessible roles and names your users actually depend on, so a semantic regression fails the build the same day it lands? Get early access, or read how resilient locators keep a suite stable while it does it.

Coming soon

Turn a described flow into a reviewable test.

Describe a flow in plain English. Generate a Playwright test, run it on your schedule, and review proposed fixes when your UI changes. Join the waitlist for early access.

  • Plain English in, real Playwright out
  • Runs in your CI, on your schedule
  • No credit card, unsubscribe anytime