axe-core Configuration & Setup
Establishing a reliable Web Accessibility Testing Fundamentals & Tool Selection pipeline begins with precise axe-core initialization. This blueprint covers installation, CLI usage, rule tagging, and CI threshold enforcement for engineering teams.
Automated accessibility checks fail when configuration drifts from WCAG requirements or when CI exit codes are improperly mapped. Proper setup ensures deterministic scans, predictable failure states, and actionable violation reports.
Environment Setup & Dependency Pinning
Install axe-core via your package manager with strict semantic version constraints. Unpinned dependencies frequently trigger sudden rule-set updates that break CI pipelines.
Node.js 20+ is required for modern axe-core rule sets and ES module compatibility (Node 18 reached end-of-life in April 2025). Install the core library and the standalone CLI tool separately to decouple runtime execution from build dependencies.
# Pin core library to prevent unexpected rule changes
npm install --save-dev axe-core@4.12.0
# Install the official CLI as a dev dependency for CI runners
npm install --save-dev @axe-core/cli@4.11.3
# Verify installation and runtime compatibility
npx axe --version
Run npx axe --version immediately after installation. This confirms the Node runtime can parse the bundled rule engine and validates CLI path resolution before integrating with CI workflows.
Rule Configuration & Context Filtering
Define axe.run() parameters to scope scans, adjust WCAG conformance levels, and suppress irrelevant checks. Framework-specific implementations like How to Configure axe-core for React and Vue Applications require targeted include/exclude selectors to avoid scanning hydration placeholders or shadow DOM artifacts.
Use runOnly to restrict execution to specific WCAG tiers. Override individual rules with the rules object. Filter output by impact severity to prioritize engineering effort.
const axe = require('axe-core');
const results = await axe.run(document, {
runOnly: {
type: 'tag',
values: ['wcag2aa', 'best-practice']
},
rules: {
'duplicate-id': { enabled: false },
'color-contrast': { enabled: true }
},
resultTypes: ['violations', 'incomplete'],
include: [['main'], ['[role="dialog"]']],
exclude: [['.skip-a11y-scan']]
});
The resultTypes array limits output to actionable data. incomplete results flag elements axe-core cannot evaluate confidently, requiring manual QA review. include/exclude arrays accept CSS selectors wrapped in arrays to isolate specific application regions.
CI/CD Gating & Threshold Mapping
Integrate axe-core into GitHub Actions or GitLab CI to enforce PR merge thresholds. The @axe-core/cli package (npx axe) scans one or more URLs and returns a non-zero exit code when violations are found.
The --exit flag forces non-zero exit codes on detected violations. Use --tags to restrict rule execution to specific WCAG conformance levels. For granular severity filtering, pipe results through a custom script rather than relying on CLI flags alone.
# .github/workflows/a11y-scan.yml
- name: Run axe-core Accessibility Scan
run: |
npx axe http://localhost:3000 --exit \
--tags wcag2aa \
--reporter json \
--stdout 2>&1 | tee a11y-results.json
env:
NODE_OPTIONS: '--max-old-space-size=2048'
CI runners parse the generated JSON to populate check annotations. Configure your pipeline to fail the job on a non-zero exit code from npx axe --exit. For gradual rollouts, wrap the scan in a Node.js script that allows a fixed number of legacy violations while blocking new regressions. Teams replacing an older URL-crawl gate should follow Migrating from Pa11y to axe-core in CI to preserve their existing baseline during the cutover.
Parallel execution across multiple URLs requires memory management. Limit concurrent axe processes to prevent OOM crashes in shared runners. Use matrix strategies with explicit concurrency controls.
Performance Tuning & False Positive Mitigation
Optimize scan duration, manage memory allocation, and implement baseline suppression. Strategies for Reducing False Positives in Automated Accessibility Scanners ensure sustainable pipeline velocity.
Standardize output formats using axe.configure(). Chunk large DOM trees to reduce synchronous blocking. Implement JSON diffing to ignore pre-approved legacy violations.
// Standardize output and configure reporter version
axe.configure({
reporter: 'v2'
});
// Baseline suppression logic
const currentResults = await axe.run(document, config);
const baseline = JSON.parse(fs.readFileSync('a11y-baseline.json', 'utf8'));
const newViolations = currentResults.violations.filter(v =>
!baseline.find(b => b.id === v.id && b.nodes.length === v.nodes.length)
);
if (newViolations.length > 0) {
console.error('New violations detected:', newViolations.map(v => v.id));
process.exit(1);
}
Set NODE_OPTIONS: '--max-old-space-size=2048' in CI environments to prevent heap exhaustion during deep DOM traversal. For SPAs, add explicit waitForLoadState('networkidle') or custom DOM stability checks before invoking axe.run(). This ensures the scanner evaluates fully rendered component trees rather than loading states.
Common Pitfalls
- Scanning hidden or off-screen elements triggers false violations. Exclude
[aria-hidden="true"]containers from the scan context. - Missing wait conditions for async component hydration leads to incomplete DOM snapshots. Always verify
document.readyStatebefore execution. - Overly strict critical-only gating blocks PRs without baseline suppression. Implement incremental thresholds during initial adoption phases.
- Unpinned axe-core versions cause sudden rule set changes and pipeline breaks. Lock dependencies in
package-lock.jsonoryarn.lock.
Frequently Asked Questions
How do I configure axe-core to only check specific WCAG conformance levels?
Use the runOnly property with type: 'tag' and an array of values like ['wcag2a', 'wcag2aa'] to restrict rule execution to targeted conformance tiers. The @axe-core/cli equivalent is --tags wcag2a,wcag2aa.
What CLI flags enforce CI pipeline failure on violations?
Use --exit to trigger a non-zero exit code when any violation is detected. Combine with --tags to restrict which rules run. For severity-based filtering (e.g., fail only on critical), wrap results in a Node.js script that reads the JSON output and applies custom logic.
How can I suppress known legacy violations without disabling rules globally?
Generate a baseline JSON report of existing violations, implement a diffing script in CI, and use rules: { id: { enabled: false } } scoped to specific selectors or components. The diffing approach is safer than disabling rules because it preserves detection of violations in new code.
Related
- Web Accessibility Testing Fundamentals & Tool Selection — The parent section comparing axe-core against Playwright, Cypress, Lighthouse, and Pa11y.
- How to Configure axe-core for React and Vue Applications — Framework-specific timing and selector scoping built on this base config.
- Reducing False Positives in Automated Accessibility Scanners — Safe suppression patterns that keep this gate from going noisy.
- Setting Up axe-core in a Next.js Monorepo — Wiring this configuration across multiple packages and shared test runners.