Automated accessibility protection
Builds repeatable accessibility checks into shared components so later changes reveal broken names, keyboard interactions and focus behavior early.
accessibility-regression-automation
See how it helpsWhen is it useful?
Use it after fixing an accessibility problem or when a component library needs protection against the same barrier returning.
What does it help you do?
- Tests controls by accessible role and name.
- Checks dialog focus trapping, Escape and focus restoration.
- Runs accessibility scans with open overlays and validation errors.
Try asking this
Protect our shared dialog against keyboard and missing-label regressions. Open it from a real trigger, submit an invalid form and close it with Escape. The test should detect lost focus or an unnamed control.
Detailed documentation
Read the skill’s original instructions, examples and checks in their authored language.
Show less
Accessibility Regression Automation
Fixing an accessibility bug once in a template is fragile if the next feature change can silently remove an aria-label, break a modal focus trap or introduce a clickable <div>. Embed deterministic accessibility checks into the component library and automated test suite so regressions fail fast during development.
1. Query by Accessible Role & Name in Tests
Write component and E2E tests the same way a user and assistive technology interact with the UI (Testing Library / Playwright locator philosophy):
- Prefer role + accessible name queries over CSS classes or
data-testid:- Playwright:
page.getByRole('button', { name: 'Create task' }),page.getByRole('dialog', { name: 'New project' }),page.getByLabel('Project name'). - Testing Library:
getByRole('tab', { name: 'Settings', selected: true }),getByRole('combobox', { name: 'Assignee' }).
- Playwright:
- Why this prevents regressions: if a button's accessible name breaks, its
<label>becomes detached from its<input>, or a<button>is replaced with an unsemantic<div>, the functional test fails immediately.
2. Automated Keyboard & Focus Lifecycle Assertions
Do not test interactive components solely with .click() — add explicit keyboard and focus assertions for critical flows and shared primitives:
- Dialog lifecycle test:
- Focus trigger button and press
Enter. - Assert
expect(dialog).toBeVisible()and verifydocument.activeElementis inside the dialog. - Press
Tabthrough the dialog controls and verify focus wraps within the dialog without escaping to background elements. - Press
Escape, assert dialog is closed/hidden, and assertexpect(triggerButton).toBeFocused().
- Focus trigger button and press
- Composite widget navigation test:
- For tabs, menus and comboboxes, dispatch
ArrowRight/ArrowDown,Home,EndandEnterkey events and assert botharia-selected/aria-expandedstates and:focustarget changes.
- For tabs, menus and comboboxes, dispatch
- Form validation test:
- Submit an invalid form via
Enterand assertaria-invalid="true"on the field, that the error message is linked viaaria-describedby, and that focus moves to the first invalid field or error summary.
- Submit an invalid form via
3. Integrating axe-core Across Component States
Running an axe scan only on the initial page load misses 80% of interactive UI:
- Run
@axe-core/playwright(orvitest-axe/jest-axe) against each meaningful state in an E2E or component test:- Initial page render.
- With dropdown menu or popover open.
- With modal dialog open.
- With form validation errors displayed.
- In both light and dark themes (where supported) to catch theme-specific contrast failures.
- Scope scans to the opened overlay or component when testing isolated widgets, and never disable core
axerules (color-contrast,button-name,label,aria-valid-attr) globally to hide failures.
4. Static Linting & Shared Primitive Guardrails
- Enable and respect static template/JSX accessibility lint rules (
eslint-plugin-jsx-a11y,eslint-plugin-vuejs-accessibility,@angular-eslint/template/accessibility). - Enforce accessibility at the design-system primitive level: require accessible names or labels in component TypeScript types (e.g., an
IconButtoncomponent whose props requireariaLabel: stringat compile time so an unlabeled icon button cannot compile).
Included files 1
SKILL.md
