Integrating AI-Powered WCAG Scanning into Your CI/CD Pipeline

  • DevOps
  • CI/CD
  • API
  • Accessibility
  • WCAG

Why Accessibility Testing Belongs in CI/CD

Most teams wouldn’t ship without unit tests, but accessibility checks often wait for a manual audit weeks after launch. A missing alt attribute or a broken focus order is cheap to fix in the pull request that added it. Once it’s in production, someone has to find it, file it and come back to code they no longer remember.

Running checks in the pipeline means issues show up in the pull request that caused them.

The cost of finding issues late

A rough picture of how the cost grows:

  • Development: a developer fixes a contrast issue in 5 minutes
  • QA: a tester files a bug and a developer switches back to fix it, about 30 minutes
  • Post-launch: triage, remediation planning and regression testing, which can take days

A check that runs on every pull request keeps most issues in the first bucket.

What AccessHawk Scans For

Each scan has three stages.

1. Browser automation with Playwright

Every scan loads your page in Chromium, so content rendered by JavaScript is included. AccessHawk captures:

  • A full-page screenshot for visual review (color contrast, text size, layout)
  • The accessibility tree, the structure screen readers use to navigate your page
  • The HTML for structural checks (heading hierarchy, landmark regions, form labels)

2. Lighthouse accessibility audit

Google Lighthouse runs its accessibility audit, a set of rule-based checks built on axe-core. It catches clear-cut problems like:

  • Images without an alt attribute
  • Text with too little color contrast
  • Form fields without labels, and missing or invalid ARIA attributes
  • Headings that skip levels
  • Positive tabindex values that change the tab order

3. AI review

Claude, Anthropic’s AI model, reviews the screenshot, accessibility tree, HTML and Lighthouse results together. It can flag issues that need judgment, which rule-based checks can’t make:

  • Decorative images marked up as informative, or the reverse
  • Ambiguous link text (“click here”, “read more”) with no surrounding context
  • Reading order that doesn’t match the visual layout
  • ARIA attributes that are valid but wrong for what the element does

Each issue includes the WCAG success criterion, a severity and a recommended fix.

How the API Integration Works

AccessHawk started as a hands-on tool: paste a URL, start a scan, read the results in your browser. That works for one-off checks, but not when you ship several times a day, so there’s now a REST API. You can start scans, fetch results and wire them into your automation without opening the dashboard.

The flow:

  1. Authenticate: use an API key generated in your account settings
  2. Submit a scan: send the target URL, a project ID and any options
  3. Get results: poll the status endpoint, or pass a webhook URL and get a POST when the scan finishes
  4. Process the response: parse the JSON results in your pipeline

A scan takes 1 to 5 minutes depending on the size of the page, so poll every 10 to 15 seconds or use a webhook. The URL has to be reachable from the public internet. If your preview deployments sit behind a login, the Team plan can send cookies or headers with the scan.

Each issue in the response has its WCAG success criterion (for example 1.4.3 Contrast (Minimum)), level, severity, location, impact and a recommendation.

For full endpoint documentation, request and response schemas, and authentication details, see the API Documentation.

Pipeline Integration Patterns

Here’s where accessibility scanning fits in a typical CI/CD workflow.

GitHub Actions

name: Accessibility Check
on:
  pull_request:
    branches: [main]

jobs:
  a11y-scan:
    runs-on: ubuntu-latest
    steps:
      - name: Wait for preview deployment
        # Wait for your preview/staging deployment to be ready
        run: ./scripts/wait-for-deploy.sh

      - name: Run AccessHawk scan
        env:
          ACCESSHAWK_API_KEY: ${{ secrets.ACCESSHAWK_API_KEY }}
        run: |
          # Submit scan against the preview deployment URL
          # Parse results and fail the check if critical issues found
          ./scripts/accesshawk-scan.sh "$PREVIEW_URL"

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

GitLab CI

accessibility_scan:
  stage: test
  needs: [deploy_staging]
  script:
    - ./scripts/accesshawk-scan.sh "$STAGING_URL"
  artifacts:
    paths:
      - a11y-results.json
    when: always
  rules:
    - if: $CI_MERGE_REQUEST_ID

General pattern

Whatever CI platform you use, the steps are the same:

  1. Deploy to a preview or staging environment
  2. Run the AccessHawk scan against that URL
  3. Parse the JSON results
  4. Apply your threshold rules (for example, fail on critical or high issues)
  5. Post a summary as a PR comment or build annotation
  6. Archive the full report as a build artifact

Working with Scan Results

A completed scan returns JSON with an overallScore (the percentage of checked WCAG criteria with no issues found), a lighthouseScore, issue counts in statistics, a reportUrl and an issues array. Each issue has these fields:

  • id: the issue ID, such as A-001
  • title: a short description of the issue
  • wcagCriteria: the success criterion, such as 1.4.3 Contrast (Minimum)
  • wcagLevel: A, AA or AAA
  • severity: critical, high, medium or low
  • location: a CSS selector or a description of where the issue is
  • impact: which criterion fails and why
  • userImpact: who is affected and how
  • recommendation: how to fix it
  • fixPriority: Immediate, High Priority, Medium Priority or Low Priority
  • resolved: whether the issue has been marked resolved

With those fields, the pipeline logic is short:

// Example: fail the build on critical or high-severity issues
const fs = require('fs');

const results = JSON.parse(fs.readFileSync('a11y-results.json', 'utf-8'));
const blocking = results.issues.filter(
  (issue) => issue.severity === 'critical' || issue.severity === 'high'
);

if (blocking.length > 0) {
  console.error(`Found ${blocking.length} blocking accessibility issues:`);
  blocking.forEach((issue) => {
    console.error(`  [${issue.severity}] ${issue.wcagCriteria}: ${issue.title} (${issue.location})`);
  });
  process.exit(1);
}

For the full response schema, see the API Documentation.

Getting Started

API access is on the Hobby, Pro and Team plans, each with its own monthly scan allowance (see the pricing page). The free Starter plan includes 2 scans in the browser but no API access.

To set it up:

  1. Create an account: sign up if you haven’t already
  2. Generate an API key: on the Settings page, create a key for CI use
  3. Read the API docs: the API Documentation covers endpoints, authentication, request and response schemas, and rate limits
  4. Write your integration script: use the patterns above as a starting point
  5. Add your API key as a CI secret: store it as ACCESSHAWK_API_KEY in your CI platform’s secrets

Best Practices

Scan staging before production

Scan a preview or staging deployment first, so you can fix issues before they ship. It’s still worth scanning production too, since that’s the only way to confirm what real users get.

Add scans to pull request checks

Make the accessibility scan a required check on pull requests. That keeps regressions out of your main branch and gives developers feedback while the change is still fresh. Since a scan takes a few minutes, run it in parallel with your other checks.

Set severity thresholds

Not every issue needs to block a deployment. One approach:

  • Critical and high issues: block the merge or deploy
  • Medium issues: warn in the PR comment but don’t block
  • Low issues: track for a later sprint

Combine with manual audits

Automated scanning, including the AI review, misses things and can get things wrong, so verify findings before you act on them. Use AccessHawk for routine checks and plan periodic manual audits for complex interactions, dynamic content and full user flows.

Watch the numbers over time

Keep the score and issue count from each run (the build artifact works for this). If the issue count keeps creeping up, set aside time to fix it before the backlog grows.