DEV Community

Rizwan Saleem
Rizwan Saleem

Posted on

Building a Comprehensive Accessibility Testing Framework for Web Applications

Building a Comprehensive Accessibility Testing Framework for Web Applications

Building a Comprehensive Accessibility Testing Framework for Web Applications

Accessibility is not an add-on feature; it’s a core quality attribute that affects every user, including people with disabilities. This tutorial walks you through designing, implementing, and maintaining a practical, scalable accessibility testing framework for modern web apps. You’ll learn how to combine automated tests, manual checks, and continuous integration to catch accessibility issues early and prevent regressions.

Why build a dedicated accessibility testing framework?

  • Accessibility problems are often hidden until late in the release cycle.
  • Automated tests catch many issues consistently, but they can miss context-specific problems.
  • A structured framework ensures accessibility is treated as a shared responsibility across design, frontend, and QA.

Key goals:

  • Detect common accessibility violations early.
  • Provide actionable guidance to developers.
  • Integrate smoothly with existing CI/CD pipelines.
  • Maintain continuity as the product evolves.

    Outline of the framework

  • Scope and standards

  • Test pyramid for accessibility

  • Automation suite

  • Manual testing practices

  • Continuous integration and reporting

  • Accessibility at design time

  • Maintenance and governance

    Scope and standards

  • Base standard: WCAG 2.1/2.2 at least AA level. Include ARIA best practices where appropriate.

  • Focus areas:

    • Perceivable: text alternatives, color contrast, scalable text
    • Operable: keyboard navigation, focus management, timeouts
    • Understandable: clear content, predictable UI
    • Robust: compatibility with assistive technologies
  • Documentation: create a living accessibility policy outlining how issues are reported, tracked, and remediated.

    Test pyramid for accessibility

  • Unit-level checks (fast, developer-facing)

    • Focus management on components
    • ARIA attribute presence and correctness
    • Keyboard interaction mappings
  • Integration/end-to-end checks

    • Page-level scans for common issues (contrast, alt text, semantic HTML)
  • Manual/exploratory testing

    • Real-screenreader testing
    • Keyboard-only navigation
  • Continuous monitoring

    • Automated tests run on CI
    • Periodic audits with human reviewers ### Automation suite: tooling and setup

Choose a stack you’re comfortable with. The examples here use a modern React project with Node.js tooling, but the concepts apply broadly.

  • Core tools
    • Axe-core or Axe-core-integrated libraries for automated accessibility testing
    • Pa11y or Lighthouse accessibility audits for automated checks
    • Playwright or Cypress for end-to-end automation with accessibility hooks
  • Example: Lighthouse CI for continuous monitoring
  • Example: Axe-core with Playwright for component-level checks

    1) Installing the tooling

  • Install Playwright and Axe

    • npm install -D @playwright/test axe-core
  • Optional: Lighthouse CI

    • npm install -D lighthouse lighthouse-ci ### 2) Component-level accessibility test (Playwright + Axe)

Create a reusable helper to run Axe checks on rendered components.

Code snippet (TypeScript):

// tests/accessibility/axe.ts
import { Page } from '@playwright/test';
import axeSource from 'axe-core';

export async function runAxeOnPage(page: Page, selector: string = 'body') {
  // Inject axe-core into the page
  await page.addScriptTag({ content: `${axeSource.toString()};` });
  // Run Axe on the given selector
  const result = await page.evaluate(async (sel) => {
    // @ts-ignore
    const results = await window.axe.run(sel);
    return results;
  }, selector);
  return result;
}
Enter fullscreen mode Exit fullscreen mode

Usage in a component test:

import { test, expect } from '@playwright/test';
import { runAxeOnPage } from './axe';

test.describe('Button component accessibility', () => {
  test('button has accessible name and keyboard support', async ({ page }) => {
    await page.goto('http://localhost:3000/button');
    const results = await runAxeOnPage(page, '#primary-button');
    expect(results.violations.length).toBe(0);
  });
});
Enter fullscreen mode Exit fullscreen mode

Notes:

  • Ensure elements have descriptive text or aria-labels.
  • Check keyboard focus behavior in tests where applicable. ### 3) Page-level audits with Lighthouse (inclusive of accessibility)

Lighthouse provides static checks for many WCAG criteria.

Example script to run Lighthouse programmatically:

// lighthouse-run.js
const lighthouse = require('lighthouse');
const { launchChrome } = require('chrome-launcher');
const fs = require('fs');

(async () => {
  const chrome = await launchChrome({ chromeFlags: ['headless'] });
  const options = { port: chrome.port, logLevel: 'info' };
  const runnerResult = await lighthouse('http://localhost:3000/', options, null);

  const report = runnerResult.lhr;
  fs.writeFileSync('lighthouse-report.json', JSON.stringify(report, null, 2));

  await chrome.kill();
})();
Enter fullscreen mode Exit fullscreen mode

CI integration tip: run Lighthouse via a headless browser in CI and fail builds if accessibility score drops below a threshold (e.g., 90).

4) End-to-end accessibility checks with Playwright

Capture common issues during flow, such as modal dialogs, dynamic content, and focus traps.

Example:

// tests/accessibility/modals.ts
import { test, expect } from '@playwright/test';
import { runAxeOnPage } from './axe';

test('sign-up modal is accessible', async ({ page }) => {
  await page.goto('http://localhost:3000');
  await page.click('#open-signup');
  // Ensure modal is visible and trap focus
  const modal = await page.locator('#signup-modal');
  await expect(modal).toBeVisible();
  const results = await runAxeOnPage(page, '#signup-modal');
  expect(results.violations.length).toBe(0);
});
Enter fullscreen mode Exit fullscreen mode

Manual testing practices

Automated tests cover many cases, but human judgment is essential.

  • Screen reader testing
    • Use NVDA/JAWS on Windows or VoiceOver on macOS for representative scenarios.
    • Verify that changes in content are announced in a sensible order.
  • Keyboard navigation
    • Test tab order, visible focus indicators, and skip links.
  • Color contrast checks

    • Use simulated color blindness tools or browser extensions to verify contrast ratios meet WCAG AA criteria (4.5:1 for body text, 3:1 for large text).
  • Check for semantic HTML

    • Ensure headings, lists, tables, and landmarks are used appropriately.
    • Confirm labels are associated with inputs via for/id attributes or aria-labelledby. ### Continuous integration and reporting
  • Integrate automated checks into your pipeline:

    • Run unit/component Axe tests on PRs.
    • Run page-level Lighthouse accessibility audits on deploy previews.
    • Fail builds if violations exceed a threshold or if critical issues are detected.
  • Reporting conventions:

    • Keep a centralized accessibility dashboard (e.g., in your CI run summaries).
    • Attach actionable details: element selectors, ARIA attributes involved, and suggested fixes.
    • Include status: compliant, warning, or fail for quick triage.
  • Issue tracking

    • When violations are found, create reproducible issues with:
    • Steps to reproduce
    • Screenshots or screen recordings
    • Affected WCAG criterion
    • Suggested remediation ### Design-time accessibility considerations
  • Design systems

    • Supply accessible components with clear focus styles, proper roles, and keyboard interactions.
    • Include accessibility tokens (e.g., contrast-checked color tokens) in design handoffs.
  • UI patterns

    • For dynamic components (accordions, tabs, dialogs), provide proper ARIA attributes and keyboard behavior out of the box.
  • Documentation

    • Add accessibility examples in design documentation and component demos.
    • Include a checklist for accessibility in design reviews. ### Maintenance and governance
  • Ownership

    • Assign an accessibility owner or committee to oversee standards and issue triage.
  • Training

    • Provide ongoing training for developers, testers, and designers.
  • Regression protocol

    • Treat accessibility regressions as high-priority bugs.
    • Include automated checks in PR reviews and require passing tests before merging.
  • Accessibility score audits

    • Schedule periodic manual audits (quarterly) to catch issues automated tests miss. ### Practical example: integrating with a React project
  • Project layout

    • tests/
    • accessibility/
      • axe.ts
      • modals.ts
    • cypress or Playwright tests for E2E accessibility
    • ci/
    • .github/workflows/ci.yml with steps:
      • install dependencies
      • run unit Axe tests
      • run Lighthouse on preview
      • publish report artifacts
  • Example CI snippet (GitHub Actions)

name: Accessibility CI

on:
  pull_request:
    branches: [ main, master ]

jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: '18'
      - name: Install
        run: npm ci
      - name: Run unit Axe tests
        run: npm run test:accessibility:unit
      - name: Build and test Lighthouse
        run: npm run test:accessibility:e2e
Enter fullscreen mode Exit fullscreen mode
  • Example npm scripts in package.json
{
  "scripts": {
    "test:accessibility:unit": "playwright test tests/accessibility/**/*.ts",
    "test:accessibility:e2e": "node lighthouse-run.js",
    "lint": "eslint ."
  }
}
Enter fullscreen mode Exit fullscreen mode

Common pitfalls and how to avoid them

  • Pitfall: Relying solely on automated checks
    • Solution: Pair automated tests with manual checks and real-world assistive technology testing.
  • Pitfall: Overly strict thresholds
    • Solution: Start with a sensible baseline (e.g., 90 Lighthouse score) and adjust as your product matures.
  • Pitfall: Slow test suites

    • Solution: Parallelize tests, run critical checks on PRs, and schedule full audits in nightly runs. ### Quick-start checklist
  • Define WCAG scope and acceptance criteria for your project.

  • Integrate Axe-based tests for components and pages.

  • Add Lighthouse accessibility audits to CI for previews.

  • Implement end-to-end accessibility tests with keyboard navigation and screen-reader-relevant flows.

  • Establish design-time guidance and accessibility reviews in the design system.

  • Set up a dashboard and ticketing workflow for accessibility issues.

  • Schedule regular manual audits and keep documentation up to date.

    If you’d like, I can tailor this framework to your tech stack (React, Vue, Angular, Svelte, or a non-JS backend-focused app) and your CI environment. Do you want a minimal starter project with a ready-to-run example, or a more extensive blueprint with templates for your team’s conventions?

-

Rizwan Saleem | https://rizwansaleem.co

Sources

Top comments (0)