Axe Testing
Automated accessibility testing with axe-core, including CI integration, custom rules, and result analysis
You are an expert in axe-core and automated accessibility testing for building accessible web applications. ## Key Points - **critical**: Blocks access entirely (e.g., images with no alt text, form inputs with no label). - **serious**: Significantly impairs use (e.g., insufficient color contrast). - **moderate**: Causes difficulty (e.g., missing landmark regions). - **minor**: Inconvenience (e.g., redundant ARIA roles). - Run axe in CI on every pull request so new violations block merges. - Use `withTags(['wcag2a', 'wcag2aa'])` to scope checks to the target conformance level. - Review `incomplete` results manually—they flag issues axe cannot conclusively determine (e.g., images that may or may not need alt text). - Complement axe with manual testing: keyboard navigation, screen reader walkthrough, and zoom/reflow testing. - Treat axe violations as test failures that block deployment, just like broken unit tests. - Scope axe runs to specific components or regions with `.include()` and `.exclude()` for targeted feedback. - Document any rule disablements with a justification and a link to the manual test that covers the gap. - Assuming a clean axe report means the page is fully accessible—automated tools only catch a fraction of issues.
skilldb get accessibility-skills/Axe TestingFull skill: 188 linesaxe-core Testing — Web Accessibility
You are an expert in axe-core and automated accessibility testing for building accessible web applications.
Core Philosophy
Overview
axe-core is an open-source accessibility testing engine maintained by Deque Systems. It powers the most widely used automated a11y tools, including the axe browser extension, axe Linter, and integrations for Playwright, Cypress, Jest, and Storybook. Automated testing catches approximately 30-40% of WCAG issues, making it a critical first layer in an accessibility testing strategy.
Core Concepts
What axe-core tests
axe-core runs a set of rules against the DOM and reports violations, passes, incomplete results (needs manual review), and inapplicable rules. Each violation includes the impacted element, the WCAG success criteria, a description of the issue, and a suggested fix.
Result categories
| Category | Meaning |
|---|---|
| violations | Definite accessibility failures |
| passes | Elements that pass the rule checks |
| incomplete | Elements that need manual review |
| inapplicable | Rules that do not apply to the page |
Impact levels
- critical: Blocks access entirely (e.g., images with no alt text, form inputs with no label).
- serious: Significantly impairs use (e.g., insufficient color contrast).
- moderate: Causes difficulty (e.g., missing landmark regions).
- minor: Inconvenience (e.g., redundant ARIA roles).
Implementation Patterns
axe-core with Playwright
// playwright.config.js — install @axe-core/playwright
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test('homepage has no critical a11y violations', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag22aa'])
.analyze();
expect(results.violations).toEqual([]);
});
test('login form is accessible', async ({ page }) => {
await page.goto('/login');
const results = await new AxeBuilder({ page })
.include('#login-form')
.withTags(['wcag2aa'])
.analyze();
expect(results.violations).toEqual([]);
});
axe-core with Cypress
// cypress/e2e/a11y.cy.js — install cypress-axe
describe('Accessibility', () => {
beforeEach(() => {
cy.visit('/');
cy.injectAxe();
});
it('has no detectable a11y violations on load', () => {
cy.checkA11y(null, {
runOnly: {
type: 'tag',
values: ['wcag2a', 'wcag2aa'],
},
});
});
it('navigation menu is accessible when expanded', () => {
cy.get('[data-testid="menu-toggle"]').click();
cy.checkA11y('#main-nav');
});
});
axe-core with Jest and jsdom
// __tests__/component.a11y.test.js
import { axe, toHaveNoViolations } from 'jest-axe';
expect.extend(toHaveNoViolations);
test('Button component has no a11y violations', async () => {
const container = document.createElement('div');
container.innerHTML = `
<button type="button">Save changes</button>
`;
document.body.appendChild(container);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
CI pipeline integration
# .github/workflows/a11y.yml
name: Accessibility Tests
on: [pull_request]
jobs:
a11y:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm run build
- run: npx serve -l 3000 dist &
- run: npx wait-on http://localhost:3000
- run: npx playwright test --project=a11y
Custom axe configuration
const axeConfig = {
rules: {
// Disable a specific rule with documented justification
'color-contrast': { enabled: false }, // handled by design tokens
// Override the impact level
'region': { enabled: true },
},
// Only run WCAG 2.2 AA rules
runOnly: {
type: 'tag',
values: ['wcag2a', 'wcag2aa', 'wcag22aa', 'best-practice'],
},
};
const results = await new AxeBuilder({ page })
.options(axeConfig)
.analyze();
Testing & Validation
- Run axe in CI on every pull request so new violations block merges.
- Use
withTags(['wcag2a', 'wcag2aa'])to scope checks to the target conformance level. - Review
incompleteresults manually—they flag issues axe cannot conclusively determine (e.g., images that may or may not need alt text). - Complement axe with manual testing: keyboard navigation, screen reader walkthrough, and zoom/reflow testing.
Best Practices
- Treat axe violations as test failures that block deployment, just like broken unit tests.
- Scope axe runs to specific components or regions with
.include()and.exclude()for targeted feedback. - Document any rule disablements with a justification and a link to the manual test that covers the gap.
Common Pitfalls
- Assuming a clean axe report means the page is fully accessible—automated tools only catch a fraction of issues.
- Disabling rules without documenting the reason or providing compensating manual test coverage.
Anti-Patterns
Over-engineering for hypothetical scale. Building for millions of users when you have hundreds adds complexity without value. Solve today's problems first.
Ignoring the existing ecosystem. Reinventing functionality that mature libraries already provide well wastes time and introduces unnecessary risk.
Premature abstraction. Creating elaborate frameworks and utilities before you have enough concrete cases to know what the abstraction should look like produces the wrong abstraction.
Neglecting error handling at boundaries. Internal code can trust its inputs, but system boundaries (user input, APIs, file I/O) require defensive validation.
Skipping documentation for obvious code. What is obvious to you today will not be obvious to your colleague next month or to you next year.
Install this skill directly: skilldb add accessibility-skills
Related Skills
Accessible Forms
Accessible form design patterns including labels, validation, error handling, and multi-step forms
Aria Patterns
ARIA roles, states, and properties for building accessible custom widgets and UI components
Color Contrast
Color contrast ratios, visual accessibility, and inclusive design for users with low vision or color blindness
Focus Management
Focus management strategies for single-page applications, modals, route changes, and dynamic content
Keyboard Navigation
Keyboard navigation patterns, focus order, and shortcut design for fully keyboard-accessible interfaces
Screen Reader Compat
Building web content that works correctly with screen readers like NVDA, JAWS, and VoiceOver