Automating Accessibility Compliance with Cypress
Run axe checks inside Cypress so accessibility regressions fail locally and in CI before they reach users.
Automating Accessibility Compliance with Cypress
Run accessibility checks inside your Cypress tests so regressions fail locally and in CI.
Introduction
Manual accessibility audits are still necessary, but they cannot catch every regression after every change. Automated checks in Cypress catch repeat issues early, especially missing labels, color-contrast failures, broken ARIA relationships, and keyboard traps that appear during common user flows.
Why Automate Accessibility Testing?
- Early Detection: Find color-contrast failures, missing ARIA attributes, and keyboard navigation gaps before they reach production.
- Continuous Checks: Block pull requests when detectable accessibility regressions appear.
- Repeatable Coverage: Run the same checks against multiple pages, components, and viewports.
Setup: Cypress + cypress-axe
-
Install dependencies
npm install --save-dev cypress cypress-axe axe-core # or yarn add --dev cypress cypress-axe axe-core -
Register in support file
In cypress/support/e2e.js:
import 'cypress-axe'; beforeEach(() => { cy.injectAxe(); });
Writing Your First Accessibility Tests
// cypress/e2e/accessibility.spec.js
describe('Accessibility Compliance', () => {
it('Home page has no detectable a11y violations', () => {
cy.visit('/');
cy.checkA11y();
});
it('Component Gallery meets WCAG 2.1 AA', () => {
cy.visit('/components');
cy.get('.component-card').each(($el) => {
cy.wrap($el).checkA11y(null, {
runOnly: {
type: 'tag',
values: ['wcag2aa']
}
});
});
});
});
- cy.injectAxe() initializes axe-core.
- cy.checkA11y() runs the audit.
- Use runOnly to narrow scope to specific WCAG levels.
Customizing Rules & Reporting
Suppress known issues or focus on specific rules:
cy.checkA11y(
null,
{ rules: { 'color-contrast': { enabled: false } } },
(violations) => {
violations.forEach(({ id, description, nodes }) => {
console.log(`${id}: ${description}`, nodes);
});
}
);
You can export violations to JSON or send them to a custom reporter when your team needs longer-term trend data.
Integrating into CI
Add an npm script for headless runs:
"scripts": {
"test:e2e": "cypress run"
}
GitHub Actions example:
jobs:
e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: cypress-io/github-action@v5
with:
command: npm run test:e2e
Accessibility test failures will block the workflow, so regressions do not merge unnoticed.
Best Practices
- Baseline Audit: Start with a manual audit so automation is checking known workflows, not guessing at coverage.
- Component-Level Tests: Isolate complex widgets so ARIA roles and keyboard behavior get direct coverage.
- Visual Testing: Combine axe checks with visual diff tools to catch missing focus indicators.
- Team Practice: Share audit reports and pair on fixes so the same mistakes do not keep returning.
Conclusion
Automated Cypress checks do not replace manual accessibility testing, but they do catch repeat regressions quickly. Start with critical pages, run the checks in CI, and expand coverage as the suite proves useful.