QFlowLearn: QTI 3.0 authoring
Automating Accessibility Compliance with Cypress
March 23, 2025 Accessibility

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

  1. Install dependencies

    npm install --save-dev cypress cypress-axe axe-core
    # or
    yarn add --dev cypress cypress-axe axe-core
  2. 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.

Related Articles

A Native TypeScript QTI 3 Player
May 20, 2026 Open Source

A Native TypeScript QTI 3 Player

A strict TypeScript QTI 3 runtime and native Web Component player with typed models, response processing, scoring, saved state, fixtures, and Playwright-tested rendering.

Work with Longsight

Bring us the problems that cross your LMS, identity, and compliance systems.

We can host the service, help you run it on campus, answer security review, integrate with your LMS, and work through accessibility requirements.