We all know accessibility (a11y) is important. It's not just about legal compliance; it's about building a web that everyone can use. But in the rush to ship features, manual accessibility checks often get skipped. "We'll fix the ARIA labels later," we say. But "later" rarely comes.
The solution isn't more discipline; it's automation.
Just as we have linters for code style and unit tests for logic, we can have automated checks for accessibility. By integrating these checks directly into your CI/CD pipeline, you can catch up to 50% of common accessibility errors before a pull request is even merged.
This guide will show you how to set up automated accessibility testing using industry-standard tools like axe-core and integrate them into a modern workflow.
The Limits of Automation (A Reality Check) 🛑
Before we start, a crucial caveat: Automated testing cannot catch everything.
Automation is great for detecting:
- Low contrast ratios
- Missing
alttext on images - Empty buttons or links
- Duplicate IDs
- Missing form labels
Automation cannot reliably detect:
- Whether the
alttext actually describes the image. - Whether the focus order makes logical sense.
- Whether your custom keyboard navigation feels intuitive.
Think of automated a11y tests like a spellchecker. It catches typos, but it can't tell you if your story is boring. For a truly accessible site, you still need manual testing and inclusive design principles, as discussed in Accessible Yet Beautiful.
Tool #1: Static Analysis with eslint-plugin-jsx-a11y
The first line of defense happens right in your editor. If you are using React, this ESLint plugin is mandatory.
It checks your JSX as you type and warns you about immediate violations.
npm install eslint-plugin-jsx-a11y --save-dev
What it catches:
// Warning: img elements must have an alt prop
<img src="logo.png" />
// Warning: Interactive elements must have a label
<button></button>
This is the cheapest form of testing because it gives instant feedback to the developer.
Tool #2: Unit Testing with jest-axe
For component-level testing, we can use jest-axe. This library wraps the powerful axe-core engine and lets you run assertions against your rendered HTML in Jest or Vitest.
import { render } from '@testing-library/react';
import { axe, toHaveNoViolations } from 'jest-axe';
import MyComponent from './MyComponent';
expect.extend(toHaveNoViolations);
it('should have no accessibility violations', async () => {
const { container } = render(<MyComponent />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
Now, every time you run your unit tests, you are also running an accessibility audit on your components.
Tool #3: E2E Testing with Cypress and cypress-axe
Unit tests are great, but they don't catch issues that arise when components interact on a real page. For that, we need End-to-End (E2E) tests.
If you are already integrating AI into your Cypress workflow, adding a11y checks is a breeze.
- Install:
npm install cypress-axe - Configure: Add
import 'cypress-axe'to your support file. - Use:
describe('Homepage', () => {
it('should be accessible', () => {
cy.visit('/');
// Inject the axe-core runtime
cy.injectAxe();
// Run the check
cy.checkA11y();
});
});
You can even configure it to ignore specific rules or elements if you have known issues you are working on fixing.
Tool #4: CI/CD Integration with Pa11y CI
For a final safety net, you can run a dedicated accessibility job in your CI pipeline using Pa11y CI. This is a command-line tool that crawls a list of URLs and reports any violations.
Step 1: Create a config file (.pa11yci)
{
"defaults": {
"standard": "WCAG2AA"
},
"urls": ["http://localhost:3000", "http://localhost:3000/about", "http://localhost:3000/contact"]
}
Step 2: Add to GitHub Actions
name: Accessibility Check
on: [push]
jobs:
a11y:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Start Server
run: npm start &
- name: Run Pa11y
run: npx pa11y-ci
If Pa11y finds any errors, the build will fail, preventing the inaccessible code from being merged.
Conclusion
Automating accessibility testing requires a small upfront investment, but the payoff is massive. It creates a "baseline of quality" that prevents regression.
By layering these tools—ESLint for the editor, Jest for components, Cypress for pages, and Pa11y for the pipeline—you create a robust safety net. This allows you to focus your manual testing efforts on the nuanced, human aspects of accessibility, ensuring a better experience for all users.
