axe-a11y
Automated accessibility scanning across the five engines - axe-core (primary), pa11y, Lighthouse a11y, WAVE, and IBM Equal Access. Authors and runs axe-core scans via the `axe.run()` JavaScript API or the @axe-core/playwright / @axe-core/cli wrappers, parses `violations[]` into per-rule severity, configures rule disable / disable-by-tag patterns, and emits CI-gateable output; references/ carry the pa11y CLI (htmlcs + axe runners), Lighthouse CI `categories:accessibility` assertions, the WAVE API / overlay, and IBM Equal Access (Section 508) with their verified CLI / API / config. Use for any automated a11y scanner setup - axe-core for JS/TS UI test suites on every PR, and the references for CLI-only, Lighthouse-pipeline, WebAIM-branded, or Section 508 scanning.
Install with skills.sh (any agent)
npx skills add testland/qa --skill axe-a11yaxe-a11y
Overview
axe-core is "an accessibility testing engine for websites and other HTML-based user interfaces" maintained by Deque (axe-core (opens in new window)). It identifies approximately 57% of WCAG issues automatically and flags items requiring human review as "incomplete" (axe-core (opens in new window)).
The integration shape: load the engine into a page (via test fixture, browser extension, or framework adapter), call axe.run(), and parse the violations[] array.
When to use
If axe-core's direct integration doesn't fit, route to the other scanners below.
Other scanners
axe-core is the primary engine; four alternatives live in references/, each with verified install / CLI / API / CI patterns:
| Scanner | Reach for it when | Reference |
|---|---|---|
| pa11y (htmlcs + axe engines) | Scriptable CLI scans without a test framework; multi-URL batching via pa11y-ci. | references/pa11y.md |
| Lighthouse a11y | The project already runs Lighthouse CI and wants a11y in the same pipeline. | references/lighthouse-a11y.md |
| WAVE | WebAIM-branded reports, visual overlay for designers, or third-party-site audits. | references/wave.md |
| IBM Equal Access | US Section 508 procurement or IBM-branded enterprise compliance. | references/ibm-equal-access.md |
All five emit JSON that a11y-violation-gate normalizes and ratchets.
Install
npm install --save-dev axe-core(Per axe-core (opens in new window).)
For framework integration (preferred over raw axe.run()):
| Package | When to use |
|---|---|
@axe-core/playwright | Playwright tests. |
@axe-core/react | React-component scans during rendering. |
@axe-core/cli | Headless CLI for arbitrary URLs. |
axe-core/api/install (raw) | Custom integrations. |
Authoring scans
Raw axe.run() API
import axe from 'axe-core';
axe.run()
.then(results => {
if (results.violations.length) {
console.error('a11y violations:', results.violations);
}
})
.catch(err => {
console.error('axe error:', err.message);
});(Adapted from axe-core (opens in new window).)
For test environments where loading axe via <script> is easier:
<script src="node_modules/axe-core/axe.min.js"></script>Playwright integration
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test('checkout page passes axe scan', async ({ page }) => {
await page.goto('/checkout');
const accessibilityScanResults = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag22aa']) // limit to AA
.analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});Cypress integration
// cypress/support/commands.js
import 'cypress-axe';
// In a test
cy.visit('/checkout');
cy.injectAxe();
cy.checkA11y();How to use
Results structure and rule configuration
axe.run() resolves to violations / incomplete / passes / inapplicable arrays; each violation carries id, impact, tags, and a nodes[] array of failing selectors. Rules are selected by WCAG tag (withTags) and suppressed by rule id or selector (disableRules / exclude). Full field tables, tag sets, and jq triage: references/results-and-config.md.
Worked example
A Playwright suite adds checkout.a11y.spec.ts. The test navigates to /checkout, runs new AxeBuilder({ page }).withTags(['wcag2a','wcag2aa','wcag22aa']).analyze(), and writes the result JSON to axe-results.json.
The run returns one entry in violations[]: id: color-contrast, impact: serious, tags including wcag2aa, and one node targeting button.primary with a failureSummary.
jq -r '.violations[] | "\(.impact): \(.id)"' axe-results.json prints serious: color-contrast. Piping the JSON to a11y-violation-gate compares it against the baseline: because this rule/selector pair is new, the gate fails the PR with the failing selector attributed. Fixing the button's foreground colour clears it, and the next run reports violations: [].
CI integration
# .github/workflows/a11y.yml
name: a11y
on:
pull_request:
push:
branches: [main]
jobs:
axe:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npx playwright install --with-deps
- name: Run a11y tests
run: npx playwright test tests/a11y/
- name: Upload report
if: always()
uses: actions/upload-artifact@v4
with:
name: a11y-results
path: playwright-report/
retention-days: 14For richer reporting, persist the raw accessibilityScanResults JSON as a build artifact and pipe to a11y-violation-gate.
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
Asserting violations.length === 0 | Existing legacy debt blocks every PR. | Use the ratchet pattern via a11y-violation-gate. |
| Disabling rules without comment in code | Reviewer can't tell which rules are intentionally off vs. forgotten. | Inline comment explaining why; quarterly review. |
| Scanning only the homepage | Most a11y bugs hide in less-traveled flows. | Scan a representative URL set: home + 1 logged-in dashboard + 1 form-heavy + 1 long-content. |
| Running axe in production | Performance overhead; possible info leak via verbose error logging. | CI / staging only. |
Treating incomplete as pass | Items needing human review go unreviewed; defects escape. | Track incomplete separately; manual review per quarter. |
| One mega-test that runs axe across every page | One failure = whole test fails; remediation hard. | One axe test per page; failure attribution clear. |
Limitations
References
IBM Equal Access accessibility-checker
View source (opens in new window)IBM Equal Access accessibility-checker
Companion reference for axe-a11y. Consult when the project ships to US federal / public-sector customers under Section 508 procurement, when an enterprise compliance program mandates IBM-branded reports, or to cross-check axe results from Selenium / Puppeteer / Playwright suites. For most projects without those constraints, direct axe (SKILL.md) is the standard recommendation - larger ecosystem, simpler integration.
IBM Equal Access provides an accessibility-checker as part of the broader Equal Access Toolkit, supporting a11y across "planning, design, development, and verification phases" (equal-access (opens in new window)). The differentiator vs. axe / pa11y / Lighthouse is US Section 508 specificity and IBM-branded enterprise rule sets.
Install
npm install --save-dev accessibility-checker(Per equal-access (opens in new window).) Cypress uses the cypress-accessibility-checker wrapper; Karma / Selenium / Puppeteer / Playwright are bundled in accessibility-checker.
Authoring scans
const { getCompliance } = require('accessibility-checker');
const results = await getCompliance(page, 'My scan label');
if (results.report.results.filter(r => r.level === 'violation').length > 0) {
process.exit(1);
}(Adapted from equal-access (opens in new window); page is a Puppeteer / Playwright page.)
Configuration (.achecker.yml)
ruleArchive: latest
policies:
- WCAG_2_2
failLevels:
- violation
- potentialviolation
reportLevels:
- violation
- potentialviolation
- recommendation
outputFormat:
- json
- html
outputFolder: a11y-reportsRule sets / policies
Per equal-access (opens in new window):
| Policy | Coverage |
|---|---|
WCAG_2_0 | WCAG 2.0 baseline. |
WCAG_2_1 | WCAG 2.1 (adds mobile / vision-related SCs). |
WCAG_2_2 | WCAG 2.2 (adds auth + drag + target-size SCs). |
IBM_Accessibility | IBM's superset including beyond-WCAG rules. |
IBM_Accessibility_2_2_2 | IBM's WCAG-2.2-aligned set. |
US Section 508 alignment is via the IBM-branded policies (the toolkit's compliance documentation maps Section 508 to specific rule combinations).
Results structure
getCompliance() returns a report with a summary.counts block and a results[] array; each result carries ruleId (e.g. WCAG20_Img_HasAlt), level, message, snippet, and a DOM path. Severity levels:
| Level | Severity |
|---|---|
violation | Definite WCAG failure. |
potentialviolation | Likely failure; needs manual review. |
recommendation | Best-practice improvement. |
potentialrecommendation | Likely improvement. |
manual | Requires manual review. |
For CI gating, fail on violation (and optionally potentialviolation); aggregate the rest at the gate.
Playwright integration
const { test, expect } = require('@playwright/test');
const { getCompliance } = require('accessibility-checker');
test('checkout passes IBM Equal Access', async ({ page }) => {
await page.goto('/checkout');
const results = await getCompliance(page, 'checkout-page');
const violations = results.report.results.filter(r => r.level === 'violation');
expect(violations).toHaveLength(0);
});For the ratchet pattern (block only on net-new violations), pipe the JSON output to a11y-violation-gate instead of asserting zero.
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
Asserting violation count is 0 | Legacy debt blocks every PR. | a11y-violation-gate ratchet. |
| Mixing Equal Access with axe in the same gate | Same issue flagged twice under different rule IDs; noise. | Run separately; cross-check at audit time. |
IBM_Accessibility policy without justification | CI fails on issues that aren't conformance failures. | Default to WCAG_2_2; add IBM policies only for IBM-branded compliance. |
Skipping the manual level | Items needing human review go unreviewed. | Track manual count; human sign-off at release. |
Limitations
References
Lighthouse CI Accessibility category
View source (opens in new window)Lighthouse CI Accessibility category
Companion reference for axe-a11y. Consult when the project already runs Lighthouse CI for Web Vitals and wants a11y coverage in the same pipeline instead of a separate scanner. If the project doesn't already use Lighthouse CI, prefer direct axe integration (SKILL.md) - Lighthouse adds a layer.
Lighthouse CI ships five audit categories: Performance, Accessibility, Best Practices, SEO, and Progressive Web App. The Accessibility category runs a curated subset of axe-core rules (lhci (opens in new window)). It audits whole pages (scored 0 - 1, rule pass rate weighted by severity); for component-level coverage, use axe in unit / integration tests.
Install and configure
npm install --save-dev @lhci/cliAdd a11y assertions to the same .lighthouserc.js used for perf, so one config drives both categories:
// .lighthouserc.js
module.exports = {
ci: {
collect: {
url: ['http://localhost:3000/', 'http://localhost:3000/checkout'],
numberOfRuns: 3,
startServerCommand: 'npm run start',
},
assert: {
assertions: {
// Accessibility - category score (0-1)
'categories:accessibility': ['error', { minScore: 0.95 }],
// Per-audit overrides - error on critical-impact a11y rules
'aria-required-attr': ['error', { minScore: 1 }],
'button-name': ['error', { minScore: 1 }],
'label': ['error', { minScore: 1 }],
'meta-viewport': ['error', { minScore: 1 }],
// Lower-impact rules - warn but don't block
'color-contrast': ['warn', { minScore: 1 }],
'image-alt': ['warn', { minScore: 1 }],
},
},
upload: { target: 'temporary-public-storage' },
},
};Per lhci (opens in new window), assertion levels are 'error' (CI fails), 'warn' (surfaced but doesn't fail), and 'off'. Run all three phases (collect / assert / upload) with npx lhci autorun.
Per-URL thresholds with assertMatrix
assert.assertions applies one threshold set to every collected URL. When pages need different bars, use assertMatrix: an array pairing a matchingUrlPattern regex with its own assertions block (lhci (opens in new window)). assertMatrix and assertions are mutually exclusive at the assert level, and the first matching pattern wins - order specific patterns before the catch-all:
assert: {
assertMatrix: [
{ matchingUrlPattern: '.*/checkout.*',
assertions: { 'categories:accessibility': ['error', { minScore: 0.98 }] } },
{ matchingUrlPattern: '.*',
assertions: { 'categories:accessibility': ['error', { minScore: 0.90 }] } },
],
},Common accessibility audit IDs
Used in assertions:; per lhci (opens in new window) (full list in Lighthouse's accessibility audit documentation):
| Audit ID | What it checks |
|---|---|
aria-allowed-attr | ARIA attributes are valid for the element's role. |
aria-required-attr | Required ARIA attributes for the role are present. |
aria-roles | Valid ARIA roles only. |
aria-valid-attr-value | ARIA attribute values are valid. |
button-name | Buttons have accessible names. |
bypass | Skip-link or landmark for bypassing repeated content. |
color-contrast | Foreground / background contrast >= 4.5:1 (3:1 large). |
document-title | <title> is set. |
frame-title | <iframe> has a title attribute. |
html-has-lang | <html> has lang. |
image-alt | <img> has alt. |
label | Form fields have associated labels. |
link-name | Links have accessible names. |
meta-viewport | <meta name="viewport"> doesn't disable zoom. |
tabindex | No tabindex > 0. |
CI integration
# .github/workflows/lighthouse.yml
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v4
with: { node-version: '20', cache: 'npm' }
- run: npm ci
- run: npm run build
- run: npx lhci autorun
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
- if: always()
uses: actions/upload-artifact@v4
with: { name: lighthouse-reports, path: .lighthouseci/ }Anti-patterns and limits
References
pa11y - CLI scanner (htmlcs + axe engines)
View source (opens in new window)pa11y - CLI scanner (htmlcs + axe engines)
Companion reference for axe-a11y. Consult when the project needs scriptable a11y scans without a full test framework (CI cron job, docs publication, static-site CI), or when a Node-stack project wants a single-command scanner instead of framework-integrated axe tests.
pa11y is "your automated accessibility testing pal" - a Node.js CLI that runs a11y tests on a page via the command line or a programmatic API (pa11y (opens in new window)). It can use HTML CodeSniffer (htmlcs, default) or axe-core as the underlying engine. If the project already runs Playwright / Cypress with axe-core, prefer the direct axe integration in SKILL.md - pa11y adds a layer.
Install and run
npm install -g pa11y # or --save-dev per-project
pa11y https://example.com(Per pa11y (opens in new window).)
Key flags
| Flag | Effect |
|---|---|
--reporter <name> | Output format: cli (default), csv, json, html, tsv. |
--standard <name> | WCAG standard: WCAG2A, WCAG2AA (default), WCAG2AAA. |
--runner <name> | Engine: htmlcs (default) or axe; repeat to run both. |
--include-warnings | Include warning-level issues (excluded by default). |
--include-notices | Include notice-level issues. |
--ignore <rules> | Skip specific rules (comma-separated). |
--threshold <n> | Allow up to N issues before failing (exit-code gate). |
--timeout <ms> | Page-load timeout. |
--config <file> | Use a .pa11yrc config file. |
pa11y's WCAG2AA standard is 2.0/2.1; WCAG 2.2 SCs (2.4.11, etc.) need --runner axe alongside htmlcs.
Multi-URL with pa11y-ci
pa11y-ci (https://github.com/pa11y/pa11y-ci) batches a URL set from a .pa11yci config and exits non-zero if any URL exceeds threshold - the canonical CI gate signal:
{
"defaults": {
"standard": "WCAG2AA",
"runners": ["axe", "htmlcs"],
"includeWarnings": true,
"threshold": 0
},
"urls": [
"https://staging.example.com/",
"https://staging.example.com/dashboard",
"https://staging.example.com/checkout"
]
}Programmatic API and results structure
const pa11y = require('pa11y');
const results = await pa11y('https://example.com', {
standard: 'WCAG2AA',
runners: ['axe', 'htmlcs'],
includeWarnings: true,
});
console.log(results.issues);results.issues[] holds one object per finding with code, type (error / warning / notice), typeCode, selector, context, message, and runner. When both engines run, the same defect appears twice under different codes - WCAG-SC-coded from htmlcs (WCAG2AA.Principle1.Guideline1_4.1_4_3.G18.Fail), rule-coded from axe (color-contrast) - and a11y-violation-gate collapses the pair via its fingerprint field.
Worked example
pa11y --standard WCAG2AA \
--runner htmlcs --runner axe \
--include-warnings \
--reporter json \
--threshold 0 \
https://staging.example.com/checkout > pa11y-results.jsonThe run exits non-zero (threshold 0 exceeded) and pa11y-results.json holds the issues[] array for the gate.
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
Default WCAG2AA without WCAG 2.2 specifics | The htmlcs standard is 2.0/2.1; 2.2 SCs need the axe runner. | Always include --runner axe. |
| Threshold 0 on a project with debt | Every PR fails until the entire backlog is fixed. | Use a11y-violation-gate ratchet OR raise threshold incrementally. |
| Running only htmlcs | Different rule coverage than axe; misses issues. | Run both runners; deduplicate at the gate. |
| Ignoring rules without config comments | Lost institutional knowledge. | Inline justification + quarterly review. |
Limitations
References
axe-core results structure and rule configuration
View source (opens in new window)axe-core results structure and rule configuration
Results structure
axe.run() resolves to an object with four arrays (axe-core (opens in new window)):
| Field | Meaning |
|---|---|
violations | Definite issues - block this in CI. |
incomplete | Items needing human review (axe couldn't determine). |
passes | Successful checks. |
inapplicable | Rules that don't apply to this page. |
Each violation has:
| Field | Meaning |
|---|---|
id | Rule ID (e.g. color-contrast, label, aria-required-attr). |
impact | critical / serious / moderate / minor. |
tags | Includes wcag2a, wcag22aa, etc. - for severity-by-SC tagging. |
description | One-line explanation. |
help | Longer remediation guidance. |
helpUrl | Direct link to Deque's rule documentation. |
nodes | Array of failing elements with target (selector), html, failureSummary. |
Triage with jq:
# Top violations by impact
jq -r '.violations[] | "\(.impact): \(.id) - \(.description)"' axe-results.json
# Just the failing selectors per rule
jq -r '.violations[] | "\(.id):", (.nodes[].target | tostring)' axe-results.jsonRule configuration
By tags
axe ships rules tagged with conformance levels:
new AxeBuilder({ page }).withTags(['wcag2a', 'wcag2aa', 'wcag22aa'])Common tag sets:
| Tag set | Coverage |
|---|---|
['wcag2a', 'wcag2aa'] | WCAG 2.0/2.1 A + AA. Default for most teams. |
['wcag2a', 'wcag2aa', 'wcag22aa'] | Adds WCAG 2.2 AA criteria. |
['wcag2aaa'] | AAA-only (rarely the gate). |
['best-practice'] | Non-WCAG good practices. |
['experimental'] | Beta rules. |
Disable specific rules
new AxeBuilder({ page }).disableRules(['color-contrast'])For per-page disabling (e.g. a known false positive on a specific component):
new AxeBuilder({ page })
.exclude('.legacy-component') // selector exclusion
.analyze();For per-rule severity in CI gating (e.g. block on critical / serious only): handle in a11y-violation-gate using the impact field.
WAVE - WebAIM's visual-overlay scanner
View source (opens in new window)WAVE - WebAIM's visual-overlay scanner
Companion reference for axe-a11y. Consult when a regulatory audit requires WebAIM-branded reports (common in US public-sector / education compliance), when designers or non-technical reviewers need the visual overlay, when auditing a third-party site without code access, or to cross-check axe / pa11y findings. For purely automated CI gating, direct axe (SKILL.md) and pa11y are simpler and free.
WAVE (Web Accessibility Evaluation Tool) is WebAIM's flagship scanner - distinguished by a visual overlay that places icons directly on the rendered page. It runs via browser extension (manual, visual), the WAVE API (programmatic), or the commercial Stand-Alone API (self-hosted).
WAVE categorizes findings into: errors (definite WCAG failures), alerts (likely issues needing review), features (positive patterns), structural elements (landmarks, headings), HTML5 / ARIA semantics, and contrast errors.
Source-fetch note (2026-05-04): WAVE's documentation lives across
wave.webaim.organdwebaim.org/articles; the API specifics may evolve - verify the current WAVE API v3+ documentation atwave.webaim.org/apibefore authoring CI integrations against specific endpoints.
Access
| Method | Cost |
|---|---|
| Browser extension (Chrome / Firefox / Edge) | Free. |
| WAVE API | Free credits + paid tiers (per WebAIM). |
| Stand-Alone API (self-hosted server) | Commercial license. |
Manual usage (extension)
Programmatic usage (API)
curl 'https://wave.webaim.org/api/request?key=YOUR_KEY&url=https://example.com&reporttype=4'Reporttype 4 returns the full JSON: a statistics block (errorcount, alertcount, featurecount, ...) and a categories object (error / alert / feature / structure / html5 / contrast). Per category, items is keyed by WAVE issue code (e.g. alt_missing, label_missing, contrast); each entry has description, count, selectors[], and per-instance xpath / selector / html.
jq triage:
# All error-level codes + counts
jq -r '.categories.error.items | to_entries[] | "\(.key): \(.value.count)"' wave-results.json
# Failing selectors per issue
jq -r '.categories.error.items | to_entries[] | .value.selectors[] | tostring' wave-results.jsonCI integration
Capture WAVE API JSON per URL and feed it to the gate (a11y-violation-gate owns the gate logic):
- name: Run WAVE scan via API
env:
WAVE_API_KEY: ${{ secrets.WAVE_API_KEY }}
run: |
for url in https://staging.example.com/ https://staging.example.com/dashboard; do
slug=$(echo "$url" | tr '/:' '__')
curl -sS "https://wave.webaim.org/api/request?key=$WAVE_API_KEY&url=$url&reporttype=4" \
> "wave-$slug.json"
doneAnti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Treating "alerts" as errors | Alerts are flagged for human review, not auto-fail. | Block on errors; route alerts to review. |
| Storing the WAVE API key in config | Key leak; quota theft. | CI secrets only. |
| Running WAVE against production | API hits load production; possible PII leakage in scan data. | Staging / pre-prod only. |
| Using WAVE alone | Different rule coverage; misses some structural / ARIA issues. | Pair with axe for full coverage. |
| Dismissing "contrast errors" | They are SC 1.4.3 violations - definite WCAG failures. | Treat as errors; aggregate via the gate. |
Limitations
References
Related skills
a11y-violation-gate
Builds a CI gate that fails the build on **new** WCAG / a11y violations introduced by a PR while grandfathering pre-existing violations on a per-rule / per-page baseline. Aggregates verdicts from axe-core / pa11y / Lighthouse a11y / WAVE / IBM Equal Access scans. Use when a project has accumulated a11y debt and a strict "zero violations" gate would block every PR - the ratchet pattern lets the team ship while preventing regressions.
aria-authoring-patterns
Reference for the W3C ARIA Authoring Practices Guide (APG) - covers the 31 canonical interactive-widget patterns (Combobox, Dialog, Menu, Tabs, Tree, etc.), their required ARIA roles and states, the keyboard-interaction model per pattern, and the canonical-violations to watch for. Use when authoring a custom interactive widget that doesn't have a native HTML equivalent, or when reviewing one for ARIA correctness.
screen-reader-test-author
Builds the full manual-accessibility artifact surface: step-by-step screen-reader test scripts for NVDA (Windows), JAWS (Windows), VoiceOver (macOS / iOS), or TalkBack (Android) with per-step keystroke + expected announcement; per-archetype WCAG 2.2 checklists (references/wcag-checklist.md); per-widget keystroke matrices pairing expected NVDA and VoiceOver announcements with the WCAG SC each row verifies (references/widget-matrix.md); and a guided NVDA / VoiceOver session protocol that merges script + checklist into a signed pass/fail session report. Use when authoring an accessibility-acceptance test, checklist, or widget matrix the team will run before sign-off, when scripting a manual a11y audit, OR when walking a tester through a guided screen-reader session.
wcag-color-contrast
Reference for WCAG 2.2 color-contrast conformance - covers SC 1.4.3 Contrast (Minimum, AA), 1.4.6 Contrast (Enhanced, AAA), 1.4.11 Non-text Contrast (AA), and 1.4.13 Content on Hover or Focus (AA) - with the canonical contrast ratios (4.5:1 normal text, 3:1 large text and UI components), measurement formula references, and bulk design-token checking patterns. Use when designing a color palette, reviewing a component for accessibility, or auditing existing CSS for contrast violations.
wcag-compliance-reporter
Builds a per-page WCAG 2.2 compliance score report by aggregating output from one or more accessibility scanners (axe-core / pa11y / lighthouse / WAVE / IBM Equal Access), pivoting violations by Success Criterion (1.4.3 contrast, 2.4.7 focus visible, etc.), grouping by conformance level (A / AA / AAA), reporting per-page coverage gaps explicitly (the "this page wasn't scanned" failure mode), and emitting both an executive summary and a per-page drill-down. Use after a multi-page accessibility scan - pa11y-ci, axe across a sitemap, lighthouse-batch - when the team needs a shareable conformance report rather than a per-page tool dump.
wcag-keyboard-navigation
Reference catalog for WCAG 2.2 keyboard-navigation conformance - covers SC 2.1.1 (Keyboard), 2.1.2 (No Keyboard Trap), 2.1.4 (Character Key Shortcuts), 2.4.3 (Focus Order), 2.4.7 (Focus Visible), 2.4.11/2.4.12 (Focus Not Obscured) - with conformance levels (A/AA), test scripts, and per-criterion failure patterns, plus the modal focus-trap / focus-management pattern (focus-on-open, Tab-cycle, inert, Escape-closes, restore-to-trigger, native `<dialog>`) in references/. Use when authoring or reviewing keyboard-only interaction support, or a modal / drawer / popover's focus management.