Accessibility Regression Triage for Data UIs
Permalink to "Accessibility Regression Triage for Data UIs"Accessibility regressions are common in data UIs because so much of their accessibility lives in details that refactors touch: a status region rendered conditionally, a scope attribute lost in a component migration, focus restoration dropped from a delete handler. When one ships, the cost depends on how quickly it is triaged. A screen reader user who cannot approve invoices today does not benefit from a fix next quarter. A short, practised triage process turns regressions into hours of disruption rather than weeks.
This guide belongs to accessibility governance for data products.
Spec reference
Permalink to "Spec reference"Severity combines WCAG impact with user impact:
- Task blocked (e.g. SC 2.1.1 failure on a primary action, SC 2.4.3 focus lost with no recovery) — treat as a production incident.
- Task degraded (e.g. SC 4.1.3 announcement missing; workaround exists) — fix in the next release.
- Friction (e.g. extra announcement, minor contrast drop) — schedule normally.
Record the WCAG criteria affected for reporting and ACR updates, but decide urgency on impact.
When to use — and when not to
Permalink to "When to use — and when not to"Use this process for any accessibility issue that worked before and does not now — reported by users, support, QA or monitoring. For issues that never worked, use the debt register instead; they are not regressions, though they may be just as urgent.
Do not downgrade a regression because “screen reader users are a small group”. For those users, the product is broken. Do not close a regression without a test.
The misapplication: treating an accessibility regression as a normal backlog bug, fixed whenever the team gets to it, while affected users cannot do their jobs.
Annotated code example
Permalink to "Annotated code example"Finding the cause by comparing accessibility trees between the last good and first bad builds:
// ax-diff.mjs — snapshot the grid's accessible structure in two builds and diff
import { chromium } from 'playwright';
async function snapshot(url) {
const browser = await chromium.launch(); const page = await browser.newPage();
await page.goto(url); await page.getByRole('button', { name: 'Amount' }).click();
const yaml = await page.getByRole('grid', { name: 'Invoices' }).ariaSnapshot();
const status = await page.getByRole('status').textContent().catch(() => null);
await browser.close();
return { yaml, status };
}
const good = await snapshot('https://build-2041.preview.example/invoices');
const bad = await snapshot('https://build-2042.preview.example/invoices');
console.log(diff(good.yaml, bad.yaml)); // e.g. columnheader "Amount" lost sort state
console.log({ good: good.status, bad: bad.status }); // e.g. status message missing in 2042
// The regression test added before closing (SC 4.1.3)
test('sorting announces the new order', async ({ page }) => {
await page.goto('/invoices');
await page.getByRole('button', { name: 'Amount' }).click();
await expect(page.getByRole('status')).toHaveText('Sorted by Amount, ascending');
});
Keyboard & AT behaviour
Permalink to "Keyboard & AT behaviour"Reproduction details matter; record them in the triage ticket:
| Field | Example |
|---|---|
| Assistive technology | NVDA 2026.2, speech and braille |
| Browser | Firefox 143 on Windows 11 |
| Steps | Tab to grid; Enter on Amount header |
| Expected | “Amount, sorted ascending” and status message |
| Actual | Header read without sort state; no status |
| Last known good | Release 6.12 |
| Impact | Sorting unusable non-visually; workaround: sort menu still announces |
Integration context
Permalink to "Integration context"Diagnosis uses the tools in accessibility tree & API inspection, especially ARIA snapshot testing with Playwright. Many regressions in React come from the pitfalls in live regions in React without lost announcements. Issues that are not regressions go into tracking accessibility debt in component libraries.
Gotchas
Permalink to "Gotchas"“Cannot reproduce.” Accessibility issues often depend on reader settings (verbosity, browse versus focus mode). Ask for settings or a speech log before closing as not reproducible.
Fixing the symptom. Adding a second live region to “make it announce” without finding why the first stopped often creates double announcements elsewhere.
Communication. Tell affected users about workarounds and fix timelines; silence after a report erodes trust quickly.
Common mistakes
Permalink to "Common mistakes"- Prioritising by number of users rather than severity for those affected.
- Closing without a regression test.
- Not recording reproduction details.
- Fixing symptoms instead of causes.
Framework notes
Permalink to "Framework notes"Common regression sources by stack: React — conditional rendering of live regions, keys causing remounts, focus lost after state updates; Angular — change detection timing for announcements, CDK upgrades changing key handling; Vue — v-if on regions, KeepAlive interactions with focus. Grid and chart library upgrades are the single most frequent cause; pin versions and run accessibility tests on upgrade branches.
Worked example
Permalink to "Worked example"After a grid refactor, support received two tickets from screen reader users: deleting a row sent focus to the top of the page. Triage reproduced it in an hour (NVDA and VoiceOver), rated it “task degraded” because users had to navigate back through 200 rows, and bisected to a change that replaced in-place deletion with a full re-render. A hotfix restored focus to the next row; a Playwright test now deletes a row and asserts focus. The lesson — “re-renders must preserve focus” — went into the code review checklist.
Testing checklist
Permalink to "Testing checklist"Design system notes
Permalink to "Design system notes"Run the design system’s accessibility tests against product repositories on library upgrade branches, so regressions surface before release. Publish regressions and their fixes in the changelog with the affected components.
FAQ
Permalink to "FAQ"Should accessibility regressions be treated as incidents?
When they block tasks for users, yes. Use the same on-call and communication processes as other incidents.
How do we find regressions before users do?
Automated tests per state, ARIA snapshots and a short screen reader smoke test on release candidates.
Who triages?
Whoever triages other bugs, with an accessibility specialist available for severity and diagnosis.
What if the cause is in a third-party library?
Pin or roll back the library version, apply a workaround, and report upstream with a minimal reproduction.
How do we prevent repeat regressions?
Every regression adds a test and, if it reveals a pattern, a review checklist item or lint rule.
Should regressions update the ACR?
If they persist beyond a release, yes. Hotfixed regressions usually do not need ACR changes.
How do we know a fix worked for users?
Ask the reporter to confirm where possible, and verify with the same AT, browser and settings used in reproduction. Close only after both the test and the confirmation.
Related
Permalink to "Related"- ARIA snapshot testing — catching structure changes
- Tracking accessibility debt — issues that are not regressions
- Writing a screen reader smoke test with Playwright — release checks