Tracking Accessibility Debt in Component Libraries

Permalink to "Tracking Accessibility Debt in Component Libraries"

Every component library has accessibility debt: a grid whose column reordering is drag-only, a chart without keyboard navigation, a date picker whose month change is silent. The danger is not that debt exists but that it is invisible — scattered across tickets, known only to the people who found it, and rediscovered by each product team that adopts the component. A debt register makes it visible, prioritised and shrinking.

This guide belongs to accessibility governance for data products.

Spec reference

Permalink to "Spec reference"

Debt entries map to WCAG success criteria, but prioritisation should consider user impact:

  • Blocker — a task cannot be completed by some users (e.g. SC 2.1.1 failure on a primary action).
  • Major — a task is possible but with significant difficulty or a workaround (e.g. SC 4.1.3 missing sort announcement).
  • Minor — friction that does not prevent tasks (e.g. an extra tab stop).

WCAG levels (A, AA) indicate conformance impact; severity indicates user impact. Track both.

A slice of a debt register Matrix of register entries — grid column reorder, chart keyboard navigation, date picker month change, toast timing — against WCAG criterion, severity, affected products and status. A slice of a debt registerWCAGSeverityProductsStatusGrid column reorderdrag-only2.5.7, 2.1.1Blocker6In progressChart lacks keyboard points2.1.1Major4PlannedDate picker month silent4.1.3Major9Fixed in 5.2Toast auto-dismiss 4 s2.2.1Major12Planned
One row per issue, linked to criteria and products.

When to use — and when not to

Permalink to "When to use — and when not to"

Keep a register as soon as a component library has more than one consumer. Review it every release with design system and product leads.

Do not let the register replace fixing: it is a planning tool, not a parking lot. Do not track debt only in a generic issue tracker without component and severity fields; reports become impossible.

The misapplication: a register sorted by WCAG level, so a Level A issue in an obscure prop outranks a Level AA issue that blocks screen reader users on every dashboard.

Scattered tickets versus a debt register Comparison: scattered tickets hide patterns, duplicate work and cannot be reported; a register links issues to components, severity and products, and produces a burn-down. Scattered tickets versus a debt register✗ Scattered ticketsSame issue filed by five teamsSeverity unknownNo view per componentLeadership sees nothingWorkarounds forgotten✓ Debt registerOne entry, five linked productsBlocker, major, minor by impactConformance status per componentBurn-down each releaseWorkaround recorded per entry

Annotated code example

Permalink to "Annotated code example"

Store debt as data next to the components, so documentation and reports are generated:

# packages/grid/a11y-debt.yml
- id: GRID-014
  title: Column reorder requires dragging
  criteria: ["2.5.7", "2.1.1"]
  severity: blocker
  workaround: "Use Columns menu > Move left/right (since 5.1)"
  affected: [billing, analytics, admin, crm, inventory, support]
  status: in-progress
  target: "5.3"
- id: GRID-021
  title: Sort change not announced in virtualized mode
  criteria: ["4.1.3"]
  severity: major
  workaround: null
  affected: [analytics, support]
  status: planned
  target: "5.4"
// Generate a component conformance badge and a burn-down from the YAML files
const open = debts.filter(d => d.status !== 'fixed');
const byComponent = groupBy(open, d => d.component);
for (const [c, items] of Object.entries(byComponent))
  writeBadge(c, items.some(i => i.severity === 'blocker') ? 'blockers' : items.length ? 'known issues' : 'no known issues');
writeBurndown(history.concat({ release: version, blocker: count(open, 'blocker'), major: count(open, 'major'), minor: count(open, 'minor') }));

Keyboard & AT behaviour

Permalink to "Keyboard & AT behaviour"

Severity should be judged by testing with users’ tools, not by reading criteria:

Observation Severity Reason
Keyboard user cannot reorder columns at all Blocker Task impossible
Screen reader user cannot tell sort changed Major Task possible after exploring
Extra tab stop in toolbar Minor Friction only
Focus ring 2.8:1 in dark mode Major Low-vision users lose place
Chart missing table view Blocker for data tasks Information unavailable
A debt register over four releases Timeline of open debt across releases: release 5.0 with 3 blockers and 14 majors; 5.1 with 2 and 12; 5.2 with 1 and 9; 5.3 with 0 and 6. A debt register over four releases5.03 blockers, 14 majorregister created5.12 blockers, 12 majorcolumn menu workaround shipped5.21 blocker, 9 majordate picker and toasts fixed5.30 blockers, 6 majordrag alternatives complete
A visible burn-down keeps debt shrinking and funded.

Integration context

Permalink to "Integration context"

New debt is often found through accessibility regression triage for data UIs and audits (manual accessibility audits). The register feeds the remarks in VPAT and ACR for data-heavy products. Component conformance status belongs with versioning and deprecating inaccessible component APIs.

Gotchas

Permalink to "Gotchas"

Product-level debt. Some debt lives in how products use components (missing labels, wrong props). Track it in the product, linked to the component guidance it violates.

Closing without verification. Mark entries fixed only after testing with the relevant assistive technology, not when the pull request merges.

Workaround rot. Workarounds documented in the register must be tested too; they break as often as anything else.

Common mistakes

Permalink to "Common mistakes"
  • Prioritising by WCAG level instead of user impact.
  • Debt spread across trackers with no component field.
  • No workaround recorded.
  • Never reporting debt to leadership.

Framework notes

Permalink to "Framework notes"

Monorepo design systems can keep a11y-debt.yml per package and generate documentation with Storybook (a “Known accessibility issues” docs block) or a docs site. Issue trackers (Jira, GitHub Issues, Linear) can hold the same data if they have component, severity and WCAG fields; generate the burn-down from their APIs.

Worked example

Permalink to "Worked example"

A design system team supporting 15 products found the same grid issues filed 40 times across product trackers. They created a register with 23 deduplicated entries, rated by user impact, each linked to affected products and a workaround. Component docs gained a “Known accessibility issues” section generated from the register. Leadership got a one-page burn-down per release. Within four releases, blockers went from three to zero, and product teams stopped filing duplicates because they could see the status.

Reporting to leadership

Permalink to "Reporting to leadership"

Leadership needs three numbers per release, not the register itself: open blockers, open majors, and the trend since the previous release. Add one sentence on the most important change (“Drag-only column reordering resolved for all six products”) and one on the biggest remaining risk. A single chart of the burn-down, released on the same cadence as product metrics, keeps accessibility in the same conversation as performance and reliability.

Testing checklist

Permalink to "Testing checklist"

Design system notes

Permalink to "Design system notes"

Treat the register as part of the design system’s public surface: consumers deserve to know what is broken and when it will be fixed. It also turns accessibility from an occasional audit topic into a measurable product metric.

FAQ

Permalink to "FAQ"
How is accessibility debt different from bugs?

Debt includes known gaps by design (features never built accessibly) as well as bugs. Both belong in the register.

Who owns the register?

The design system team for component debt; product teams for product-level debt, with links between them.

Should the register be public?

Internally public at least. Some companies publish component conformance externally; it builds trust with customers.

How do we rate severity consistently?

Use user-impact definitions with examples, and calibrate by rating a few issues together as a team.

How often should it be reviewed?

Every release, with a quick triage of new entries weekly.

Can debt be "accepted"?

Occasionally, with a documented reason and workaround, reviewed periodically. Accepted debt still appears in ACR remarks.

What fields does a debt entry need at minimum?

An ID, title, component, WCAG criteria, severity, workaround, affected products, status and target release. Anything less makes prioritisation and reporting guesswork.

Permalink to "Related"

← Back to Accessibility Governance for Data Products