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.

Triage in five steps Flow: reproduce with the reported setup; assess impact; find the cause; choose a response; add a test and record the lesson. Triage in five stepsReproducesame AT, browser, versionswithin hoursAssess impactblocked, degraded or frictionusers and tasks affectedFind causebisect; compare accessibility treesthe change, not just the symptomRespondhotfix, rollback, flag or workaroundcommunicate to affected usersPreventregression test; lesson recordedclose only after both

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.

Backlog bug versus triaged regression Comparison: handled as an ordinary backlog bug, a blocking accessibility regression waits weeks; triaged like an incident, it is reproduced, mitigated and fixed with a test within days. Backlog bug versus triaged regression✗ Ordinary backlog bugWaits for prioritisationNo reproduction with ATCause guessedFixed eventuallyNo test added✓ Triaged regressionSeverity assessed the same dayReproduced with the reported ATChange identified by bisectHotfix, rollback or workaround nowRegression test before closing

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
A regression handled well Timeline: report at 09:10; reproduced at 10:00; severity assessed as degraded with a workaround at 10:15; cause found by tree diff at 12:30; hotfix with regression test shipped next morning; lesson added to the review checklist. A regression handled well09:10Reportsupport ticket from an NVDA user10:00ReproducedNVDA + Firefox, release 6.1310:15Assesseddegraded; workaround via sort menu12:30Cause foundstatus region now conditionally renderedNext dayFixedhotfix plus Playwright testNext weekLessonreview checklist updated
Under a day from report to fix, with a test that keeps it fixed.

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.

Permalink to "Related"

← Back to Accessibility Governance for Data Products