Skip to content
Codeloom
Web

WCAG Compliance Auditing and Automated Testing

Audit your site for WCAG 2.2 compliance using automated tools, manual testing checklists, and CI integration. Covers axe-core, Lighthouse, screen reader testing, and remediation.

·6 min read · By Codeloom
Intermediate 11 min read

What you'll learn

  • How to run a structured WCAG 2.2 compliance audit
  • Using axe-core and Lighthouse for automated accessibility testing
  • Integrating accessibility checks into CI/CD pipelines
  • Manual testing techniques that catch what automation misses
  • Prioritizing and remediating accessibility violations

Prerequisites

None — this post is self-contained.

Knowing that accessibility matters is different from knowing whether your site is actually accessible. A WCAG compliance audit answers that question systematically. It combines automated scanning tools that catch the roughly 30% of issues detectable by machines with manual testing techniques that catch the remaining 70%. This guide walks through both layers and shows how to integrate them into your development workflow.

Automated Scanning with axe-core

axe-core is the most widely used accessibility testing engine. It powers the checks in Chrome DevTools, Lighthouse, and dozens of CI tools. Run it directly in your test suite:

// accessibility.test.js
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';

test.describe('Accessibility', () => {
  test('home page has no violations', async ({ page }) => {
    await page.goto('/');

    const results = await new AxeBuilder({ page })
      .withTags(['wcag2a', 'wcag2aa', 'wcag22aa'])
      .analyze();

    expect(results.violations).toEqual([]);
  });

  test('form page has no violations', async ({ page }) => {
    await page.goto('/signup');

    // Fill the form to test dynamic states
    await page.fill('#email', 'test@example.com');
    await page.click('#submit');

    const results = await new AxeBuilder({ page })
      .withTags(['wcag2a', 'wcag2aa'])
      .exclude('.third-party-widget')  // Exclude elements you can't control
      .analyze();

    expect(results.violations).toEqual([]);
  });
});

The withTags method filters rules to specific WCAG levels. Start with wcag2a and wcag2aa for Level A and AA compliance, which covers the requirements most organizations need.

Interpreting Results

axe-core returns violations with impact levels: critical, serious, moderate, and minor. Each violation includes the failing element, the WCAG criterion it violates, and a suggested fix:

// Log detailed violation info for debugging
for (const violation of results.violations) {
  console.log(`Rule: ${violation.id}`);
  console.log(`Impact: ${violation.impact}`);
  console.log(`WCAG: ${violation.tags.filter(t => t.startsWith('wcag')).join(', ')}`);
  console.log(`Help: ${violation.helpUrl}`);
  for (const node of violation.nodes) {
    console.log(`  Element: ${node.html}`);
    console.log(`  Fix: ${node.failureSummary}`);
  }
}

Lighthouse Accessibility Audits

Lighthouse provides a scored accessibility audit that is useful for tracking progress over time:

// lighthouse-a11y.js
import lighthouse from 'lighthouse';
import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
const page = await browser.newPage();

const result = await lighthouse('http://localhost:3000', {
  port: new URL(browser.wsEndpoint()).port,
  onlyCategories: ['accessibility'],
  output: 'json',
});

const score = result.lhr.categories.accessibility.score * 100;
console.log(`Accessibility score: ${score}`);

if (score < 90) {
  console.error('Accessibility score below threshold');
  process.exit(1);
}

await browser.close();

Run this in CI to fail builds when the accessibility score drops below your target.

CI/CD Integration

Add accessibility checks to your pull request pipeline so violations are caught before they merge:

name: Accessibility
on: [pull_request]

jobs:
  a11y:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci
      - run: npm run build

      - name: Start server
        run: npm run preview &
        env:
          PORT: 3000

      - name: Wait for server
        run: npx wait-on http://localhost:3000

      - name: Run accessibility tests
        run: npx playwright test accessibility.test.js

      - name: Upload results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: a11y-report
          path: test-results/

For Storybook-based component libraries, use @storybook/addon-a11y to run axe-core on every story and storybook-addon-a11y in CI to scan all stories automatically.

Manual Testing Checklist

Automated tools cannot evaluate whether alt text is meaningful, whether focus order is logical, or whether error messages are helpful. Manual testing covers these gaps.

Keyboard Navigation

Navigate your entire application using only the keyboard:

  • Tab moves focus forward, Shift+Tab moves it backward. Every interactive element must be reachable.
  • Enter and Space activate buttons and links. Custom components must handle both.
  • Escape closes modals and popups. Focus must return to the element that triggered them.
  • Arrow keys navigate within composite widgets like menus, tabs, and listboxes.

Check that focus is always visible. If you cannot tell where focus is, your focus styles are insufficient.

Screen Reader Testing

Test with at least one screen reader:

  • macOS: VoiceOver (built-in, activated with Cmd+F5)
  • Windows: NVDA (free) or JAWS (commercial)
  • Mobile: TalkBack (Android) or VoiceOver (iOS)

Listen for these issues:

  • Images without alt text are announced as the filename, which is meaningless.
  • Form inputs without labels are announced as “edit text” with no context.
  • Dynamic content changes (like toast notifications) are not announced unless they use aria-live regions.
  • Custom components (dropdowns, date pickers) are unusable without proper ARIA roles and states.

Color and Contrast

Use a contrast checker to verify text meets the minimum ratios:

  • Normal text: 4.5:1 contrast ratio (WCAG AA)
  • Large text (18pt+ or 14pt+ bold): 3:1 contrast ratio
  • Non-text elements (icons, borders, focus indicators): 3:1 contrast ratio

WCAG 2.2 added a focus appearance criterion (2.4.11) that requires focus indicators to have a minimum area and contrast, not just be visible.

Common Violations and Fixes

Here are the most frequently failing WCAG criteria and how to fix them:

Missing Form Labels

<!-- Violation: input without a label -->
<input type="email" placeholder="Email">

<!-- Fix: associate a label -->
<label for="email">Email address</label>
<input type="email" id="email" placeholder="name@example.com">

Missing Alt Text

<!-- Violation: no alt attribute -->
<img src="chart.png">

<!-- Fix: descriptive alt text -->
<img src="chart.png" alt="Monthly revenue chart showing 15% growth in Q2">

<!-- Decorative images: empty alt -->
<img src="divider.png" alt="">

Insufficient Color Contrast

/* Violation: light gray on white = 2.5:1 */
.subtitle { color: #aaaaaa; }

/* Fix: darker gray = 4.6:1 */
.subtitle { color: #767676; }

Missing Landmark Regions

<!-- Violation: div soup -->
<div class="header">...</div>
<div class="sidebar">...</div>
<div class="content">...</div>

<!-- Fix: semantic landmarks -->
<header>...</header>
<nav aria-label="Main navigation">...</nav>
<main>...</main>
<footer>...</footer>

Prioritizing Remediation

When an audit reveals dozens of violations, prioritize by impact:

  1. Critical: Users cannot complete core tasks. Missing form labels, keyboard traps, missing page language.
  2. Serious: Users can complete tasks but with significant difficulty. Poor contrast, missing landmarks, unlabeled icons.
  3. Moderate: Users experience inconvenience. Missing skip links, redundant links, unclear focus order.
  4. Minor: Best practice issues. Missing aria-describedby for complex forms, suboptimal heading hierarchy.

Fix critical and serious issues first. Track progress by running automated scans weekly and graphing the violation count over time.

Key Takeaways

A WCAG compliance audit combines automated scanning with axe-core or Lighthouse for the 30% of issues machines can detect, and manual testing with keyboard navigation and screen readers for the 70% they cannot. Integrate automated checks into CI/CD to prevent regressions, and schedule manual audits quarterly for comprehensive coverage. Prioritize fixes by user impact, starting with issues that block core task completion. Accessibility is not a checkbox to achieve once but a quality dimension to maintain continuously.