Skip to main content
Technology & EngineeringAccessibility188 lines

Axe Testing

Automated accessibility testing with axe-core, including CI integration, custom rules, and result analysis

Quick Summary18 lines
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 lines
Paste into your CLAUDE.md or agent config

axe-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

CategoryMeaning
violationsDefinite accessibility failures
passesElements that pass the rule checks
incompleteElements that need manual review
inapplicableRules 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 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.

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

Get CLI access →