Help us improve
Share bugs, ideas, or general feedback.
From accessibility-test-scanner
Audits web apps for WCAG 2.1/2.2 compliance with axe-core/Playwright, Pa11y, Lighthouse scans plus keyboard nav, ARIA, color contrast checks.
npx claudepluginhub jeremylongshore/claude-code-plugins-plus-skills --plugin accessibility-test-scannerHow this skill is triggered — by the user, by Claude, or both
Slash command
/accessibility-test-scanner:scanning-accessibilityThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Validate web applications against WCAG 2.1/2.2 accessibility standards covering perceivability, operability, understandability, and robustness. Combines automated scanning with axe-core, Pa11y, and Lighthouse accessibility audits alongside manual validation checklists for keyboard navigation, screen reader compatibility, and color contrast.
Runs a WCAG AA accessibility audit across 7 categories (keyboard, contrast, ARIA, reduced motion, screen reader, high contrast, automated axe) with findings and fixes.
Conducts interactive WCAG accessibility audits on entire solutions, directories, or live URLs, checking compliance levels A/AA/AAA with optional Playwright visual scans.
Share bugs, ideas, or general feedback.
Validate web applications against WCAG 2.1/2.2 accessibility standards covering perceivability, operability, understandability, and robustness. Combines automated scanning with axe-core, Pa11y, and Lighthouse accessibility audits alongside manual validation checklists for keyboard navigation, screen reader compatibility, and color contrast.
@axe-core/playwright to scan after page load.role="button" on clickable divs).aria-label or aria-labelledby on elements without visible text.aria-live regions announce dynamic content changes.aria-expanded, aria-selected, and aria-checked states toggle correctly.<label> with matching for/id.aria-describedby and aria-invalid.aria-required="true" and visual indicators.| Error | Cause | Solution |
|---|---|---|
| axe-core reports no violations but page is inaccessible | Automated tools catch ~30-40% of issues; manual testing needed | Supplement automated scans with keyboard and screen reader manual testing |
| Color contrast violation on dynamic theme | Theme colors computed at runtime not captured by static scan | Run scans with each theme active (light/dark); test with high-contrast mode |
| False positive on hidden content | Scanner checks elements that are visually hidden but present in DOM | Use axe.configure({ rules: [{ id: 'rule-id', selector: ':visible' }] }) |
| ARIA role conflicts | Multiple conflicting ARIA attributes on same element | Remove redundant roles; follow WAI-ARIA authoring practices for the component pattern |
| Focus order incorrect after dynamic content | DOM insertion order differs from visual order | Use tabindex to correct order; restructure DOM to match visual layout |
Playwright + axe-core accessibility test:
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test('homepage meets WCAG AA', async ({ page }) => {
await page.goto('/');
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa', 'wcag21aa'])
.analyze();
expect(results.violations).toEqual([]);
});
test('login form is keyboard accessible', async ({ page }) => {
await page.goto('/login');
await page.keyboard.press('Tab');
const focused = await page.evaluate(() => document.activeElement?.getAttribute('data-testid'));
expect(focused).toBe('email-input');
await page.keyboard.press('Tab');
const nextFocused = await page.evaluate(() => document.activeElement?.getAttribute('data-testid'));
expect(nextFocused).toBe('password-input');
});
Pa11y CI configuration:
{
"defaults": {
"standard": "WCAG2AA",
"timeout": 10000, # 10000: 10 seconds in ms
"wait": 1000 # 1000: 1 second in ms
},
"urls": [
"http://localhost:3000/", # 3000: 3 seconds in ms
"http://localhost:3000/login", # 3 seconds in ms
"http://localhost:3000/dashboard", # 3 seconds in ms
{ "url": "http://localhost:3000/settings", "actions": ["click element #tab-profile"] }
]
}