axle · docs

axle docs

Everything you need to install, configure, and operate axle in CI. axle is one engine (axe-core 4.11) shipped through five surfaces — GitHub Action, npm CLI, Netlify / Cloudflare Pages / Vercel plugins, and a WordPress plugin. The configuration shape is identical across surfaces so you only learn it once.

Quick start

Add to .github/workflows/accessibility.yml:

name: Accessibility
on: [pull_request]
jobs:
  a11y:
    runs-on: ubuntu-24.04
    permissions:
      contents: read
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
      - uses: asafamos/axle-action@v1
        with:
          url: ${{ secrets.PREVIEW_URL }}
          fail-on: serious

That's it. The Action will scan the URL, post a sticky PR comment with violations grouped by severity, and exit non-zero if any violation crosses your fail-on threshold. AI fixes are off by default — see the AI section below to enable.

GitHub Action

Marketplace listing: axle — a11y / WCAG Accessibility CI. Source: asafamos/axle-action. Pin to @v1 for major-version stability or to a specific tag like @v1.1.0 for exact reproducibility.

Two scan modes

External URL. Pass url. Useful when your PR has a preview URL from Vercel / Netlify / Cloudflare / Render etc.

- uses: asafamos/axle-action@v1
  with:
    url: https://preview-${{ github.event.pull_request.number }}.example.com
    fail-on: serious

Build-and-serve locally. Leave url empty. The Action runs install-command build-commandstart-command in the background, waits for the port, and scans localhost:PORT.

- uses: asafamos/axle-action@v1
  with:
    install-command: npm ci
    build-command: npm run build
    start-command: npm start
    wait-on-port: "3000"
    fail-on: serious

npm CLI

Same engine as the Action, runs anywhere Node 20+ runs. Useful for GitLab / Jenkins / CircleCI / Buildkite / Bitbucket / local dev.

# one-shot
npx axle-cli scan https://example.com --fail-on serious

# with AI fixes (requires ANTHROPIC_API_KEY env)
npx axle-cli scan https://example.com \
  --fail-on serious \
  --with-ai-fixes true \
  --max-ai-fixes 10 \
  --json-out axle-report.json \
  --markdown-out axle-report.md

# install globally
npm i -g axle-cli
axle-cli scan https://example.com

Exit codes: 0 — passing at threshold. 1 — violations at or above threshold. 2 — invalid arguments / fatal error.

Netlify / Cloudflare Pages / Vercel

Each hosting plugin runs the scan against the platform's preview URL on every deploy and fails the deploy if the threshold is crossed.

Netlify

Add to netlify.toml:

[[plugins]]
package = "axle-netlify-plugin"

  [plugins.inputs]
  fail-on = "serious"

Cloudflare Pages

Add as a Worker bound to onPostDeploy. See the axle-cloudflare-plugin README.

Vercel

Add a build step in vercel.json:

{
  "buildCommand": "next build && npx axle-vercel-plugin"
}

WordPress plugin

Different model — runs in the WP admin via a hidden iframe + the bundled axe-core build. No outbound calls. Install from the WordPress.org plugin directory (search “axle” or “a11y scanner”) once approved, or upload the ZIP from the source repo.

Configuration reference

All inputs accepted by the Action (CLI uses the same flags with--kebab-case):

InputDefaultDescription
url""External URL to scan. If empty, build + start locally.
fail-onseriouscritical | serious | moderate | minor | none. Threshold above which the run fails.
with-ai-fixesfalseGenerate Claude fix diffs in the PR comment. Requires anthropic-api-key.
max-ai-fixes10Cost guard — caps Claude calls per run.
anthropic-api-keyAnthropic API key. Pass as a repository secret. Required when with-ai-fixes: true.
anthropic-modelclaude-sonnet-4-6Override the model used for fix generation.
github-tokengithub.tokenUsed to post the PR comment. Default workflow token works if you set pull-requests: write.
comment-on-prtruePost / update the sticky comment on the triggering PR.
install-commandnpm ciUsed only when url is empty.
build-commandnpm run buildUsed only when url is empty.
start-commandnpm startUsed only when url is empty. Backgrounded.
wait-on-port3000Port to wait for before scanning localhost:PORT.

Output format

Two artifacts are emitted on every run: axle-report.json (machine-readable) and axle-report.md (human-readable).

{
  "url": "https://example.com",
  "scanned_at": "2026-04-27T14:00:00.000Z",
  "engine": "axe-core@4.11.3",
  "summary": {
    "violations": 7,
    "critical": 1,
    "serious": 3,
    "moderate": 2,
    "minor": 1,
    "passes": 142
  },
  "violations": [
    {
      "id": "color-contrast",
      "impact": "serious",
      "wcag": ["1.4.3"],
      "help": "Elements must have sufficient color contrast",
      "helpUrl": "https://dequeuniversity.com/rules/axe/4.11/color-contrast",
      "nodes": [
        {
          "html": "<button class='cta'>...</button>",
          "target": [".hero > .cta"],
          "failureSummary": "Element has insufficient color contrast of 2.85"
        }
      ]
    }
  ]
}

The schema is stable across major versions. WCAG mappings come from axe-core's own metadata (we don't re-tag).

AI fixes

With with-ai-fixes: true+ an Anthropic API key, axle sends each violation's offending HTML and the rule metadata to Claude Sonnet, which returns a unified diff suggestion. The diff appears inline in the PR comment. axle never commits anything autonomously — a human reviews and merges (or edits) the suggestion.

Anthropic's API does not train on this data per their enterprise API terms. On the Business plan we add a zero-retention pass-through flag so prompts aren't logged.

The cap is max-ai-fixes (default 10) per run. Hosted AI on Team / Business plans pulls from a monthly budget — see pricing.

Common patterns

Vercel preview URL per PR

- uses: asafamos/axle-action@v1
  with:
    url: https://${{ github.event.pull_request.head.ref }}-${{ secrets.VERCEL_PROJECT }}.vercel.app
    fail-on: serious

Multiple URLs (one job per page)

jobs:
  a11y:
    strategy:
      matrix:
        path: [/, /pricing, /signup, /checkout]
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v4
      - uses: asafamos/axle-action@v1
        with:
          url: ${{ secrets.PREVIEW_BASE }}${{ matrix.path }}
          fail-on: serious

Monorepo (only run when frontend changes)

on:
  pull_request:
    paths:
      - 'apps/web/**'
      - 'packages/ui/**'

Authenticated routes

For routes that require login, run the Action against a test-user-authenticated preview URL (Playwright fixture) rather than reproducing auth in the Action itself. We're working on a lightweight cookie-pass-through option for v2.

Troubleshooting

The Action runs but fails immediately with a Chromium error

This usually means the Playwright cache wasn't restored. The first run on a runner downloads ~150MB of Chromium; subsequent runs use the cache. Add --with-deps if running on a non-Ubuntu runner — but most issues resolve by waiting through the first cold run.

No PR comment appears

Workflow needs permissions: pull-requests: write. For forked-PR workflows, the default github.token has read-only permissions; use a PAT withpull-requests: write as github-token.

Scan times out / runs too slowly

Cold runs take ~90s including browser install. If your run is materially slower, check wait-on-port— if the app isn't serving on that port, the Action waits until timeout before failing. Verify start-command actually binds to the expected port.

False positives on a known-OK pattern

axe-core rules can be disabled per-violation via data-axe-ignoreattributes — but please don't do this casually. axe-core is calibrated for zero false positives at serious+ severity. If you genuinely have a false positive, file an issue at axe-core upstream.

API key + auth

The Open plan needs no axle API key — the Action and CLI run with no axle credentials. AI fixes use your own Anthropic key directly.

Team and Business plans get an AXLE_API_KEY by email after subscription. Use it as:

  • GitHub Action: pass as axle-api-key: ${{ secrets.AXLE_API_KEY }}.
  • CLI: export AXLE_API_KEY=<key> before running.
  • Web: paste at /account once to unlock unlimited fixes in the browser scan.

Lost the key? It's in the welcome email under subject “Your axle API key”. If you can't find it, email asaf@amoss.co.il with the email used at checkout.

Install GitHub Action →axle-cli on npmPricingFree scan