axe-accessibility
You are axe-accessibility - a specialized skill for axe-core accessibility testing integration, providing comprehensive WCAG compliance validation capabilities.
Overview
This skill enables AI-powered accessibility testing including:
- Executing axe-core accessibility scans
- Interpreting WCAG violations and impacts
- Generating accessibility compliance reports
- Configuring rule inclusion/exclusion
- Integrating with Playwright/Cypress for automated a11y testing
- Handling dynamic content scanning
- Mapping violations to WCAG criteria
- Providing remediation guidance
Prerequisites
- Node.js environment for axe-core
- Test automation framework (Playwright, Cypress, or Selenium)
- Browser automation capability
- Optional: axe-playwright or axe-cypress packages
Capabilities
1. Playwright Integration
Use axe-core with Playwright for accessibility testing:
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test.describe('Accessibility Tests', () => {
test('homepage should have no violations', async ({ page }) => {
await page.goto('https://example.com');
const accessibilityScanResults = await new AxeBuilder({ page }).analyze();
expect(accessibilityScanResults.violations).toEqual([]);
});
test('homepage should have no critical violations', async ({ page }) => {
await page.goto('https://example.com');
const accessibilityScanResults = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21aa'])
.analyze();
const criticalViolations = accessibilityScanResults.violations
.filter(v => v.impact === 'critical' || v.impact === 'serious');
expect(criticalViolations).toEqual([]);
});
test('form page accessibility', async ({ page }) => {
await page.goto('https://example.com/contact');
// Wait for form to be fully loaded
await page.waitForSelector('form');
const results = await new AxeBuilder({ page })
.include('form')
.analyze();
// Log violations for debugging
if (results.violations.length > 0) {
console.log('Violations found:', JSON.stringify(results.violations, null, 2));
}
expect(results.violations).toEqual([]);
});
});
2. Cypress Integration
Use axe-core with Cypress:
// cypress/support/commands.js
import 'cypress-axe';
// cypress/e2e/accessibility.cy.js
describe('Accessibility Tests', () => {
beforeEach(() => {
cy.visit('/');
cy.injectAxe();
});
it('has no detectable accessibility violations on load', () => {
cy.checkA11y();
});
it('has no violations in main content', () => {
cy.checkA11y('#main-content');
});
it('has no critical violations', () => {
cy.checkA11y(null, {
includedImpacts: ['critical', 'serious']
});
});
it('logs violations for review', () => {
cy.checkA11y(null, null, (violations) => {
violations.forEach((violation) => {
cy.log(`${violation.id}: ${violation.description}`);
violation.nodes.forEach((node) => {
cy.log(` - ${node.target}`);
});
});
}, true);
});
});
3. Standalone axe-core Usage
Direct axe-core usage:
import { chromium } from 'playwright';
import axe from 'axe-core';
async function runAccessibilityAudit(url) {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto(url);
// Inject axe-core
await page.addScriptTag({ path: require.resolve('axe-core') });
// Run analysis
const results = await page.evaluate(async () => {
return await axe.run();
});
await browser.close();
return results;
}
// Usage
const results = await runAccessibilityAudit('https://example.com');
console.log(`Found ${results.violations.length} violations`);
4. WCAG Compliance Configuration
Configure axe for specific WCAG standards:
import AxeBuilder from '@axe-core/playwright';
// WCAG 2.1 Level AA compliance
const wcag21AAResults = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa'])
.analyze();
// WCAG 2.2 Level AA compliance
const wcag22AAResults = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'wcag22aa'])
.analyze();
// Section 508 compliance
const section508Results = await new AxeBuilder({ page })
.withTags(['section508'])
.analyze();
// Best practices (not strict compliance)
const bestPracticesResults = await new AxeBuilder({ page })
.withTags(['best-practice'])
.analyze();
5. Rule Configuration
Include/exclude specific rules:
import AxeBuilder from '@axe-core/playwright';
const results = await new AxeBuilder({ page })
// Include only specific rules
.withRules(['color-contrast', 'image-alt', 'label', 'link-name'])
.analyze();
// Or exclude rules
const resultsExcluding = await new AxeBuilder({ page })
.disableRules(['region', 'landmark-one-main'])
.analyze();
// Focus on specific element
const formResults = await new AxeBuilder({ page })
.include('#contact-form')
.analyze();
// Exclude problematic areas
const mainResults = await new AxeBuilder({ page })
.exclude('#third-party-widget')
.analyze();
6. Violation Analysis
Analyze and categorize violations:
function analyzeViolations(results) {
const summary = {
total: results.violations.length,
byImpact: {},
byWCAG: {},
criticalIssues: []
};
for (const violation of results.violations) {
// Count by impact
summary.byImpact[violation.impact] =
(summary.byImpact[violation.impact] || 0) + violation.nodes.length;
// Count by WCAG criteria
for (const tag of violation.tags) {
if (tag.startsWith('wcag')) {
summary.byWCAG[tag] = (summary.byWCAG[tag] || 0) + 1;
}
}
// Collect critical issues
if (violation.impact === 'critical' || violation.impact === 'serious') {
summary.criticalIssues.push({
id: violation.id,
impact: violation.impact,
description: violation.description,
help: violation.help,
helpUrl: violation.helpUrl,
affectedElements: violation.nodes.length
});
}
}
return summary;
}
7. Report Generation
Generate accessibility reports:
function generateA11yReport(results, format = 'html') {
const report = {
timestamp: new Date().toISOString(),
url: results.url,
summary: {
violations: results.violations.length,
passes: results.passes.length,
incomplete: results.incomplete.length,
inapplicable: results.inapplicable.length
},
violations: results.violations.map(v => ({
id: v.id,
impact: v.impact,
description: v.description,
help: v.help,
helpUrl: v.helpUrl,
wcagCriteria: v.tags.filter(t => t.startsWith('wcag')),
nodes: v.nodes.map(n => ({
target: n.target,
html: n.html,
failureSummary: n.failureSummary
}))
}))
};
if (format === 'html') {
return generateHtmlReport(report);
}
return JSON.stringify(report, null, 2);
}
function generateHtmlReport(report) {
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Accessibility Report</title>
<style>
body { font-family: -apple-system, sans-serif; max-width: 1200px; margin: 0 auto; padding: 20px; }
.violation { border: 1px solid #ddd; margin: 10px 0; padding: 15px; border-radius: 4px; }
.critical { border-left: 4px solid #d32f2f; }
.serious { border-left: 4px solid #f57c00; }
.moderate { border-left: 4px solid #fbc02d; }
.minor { border-left: 4px solid #388e3c; }
.impact { padding: 2px 8px; border-radius: 4px; font-size: 12px; }
h1 { color: #333; }
pre { background: #f5f5f5; padding: 10px; overflow-x: auto; }
</style>
</head>
<body>
<h1>Accessibility Report</h1>
<p>U