Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Conduct WCAG 2.2 accessibility audits with automated testing, manual verification, and remediation guidance. Use when auditing websites for accessibility, fixing WCAG violations, or implementing accessible design patterns.
.claude/skills/dicklesworthstone-wcag-audit-patterns/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-14 | ✗→✓ | ▲ Improved | 165% | 0% |
| case-19 | ✓→✗ | ▼ Worse | 213% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 171% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 254% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 174% | 0% |
Comprehensive guide to auditing web content against WCAG 2.2 guidelines with actionable remediation strategies.
| Level | Description | Required For | | ------- | ---------------------- | ----------------- | | A | Minimum accessibility | Legal baseline | | AA | Standard conformance | Most regulations | | AAA | Enhanced accessibility | Specialized needs |
Perceivable: Can users perceive the content?
Operable: Can users operate the interface?
Understandable: Can users understand the content?
Robust: Does it work with assistive tech?Critical (Blockers):
├── Missing alt text for functional images
├── No keyboard access to interactive elements
├── Missing form labels
└── Auto-playing media without controls
Serious:
├── Insufficient color contrast
├── Missing skip links
├── Inaccessible custom widgets
└── Missing page titles
Moderate:
├── Missing language attribute
├── Unclear link text
├── Missing landmarks
└── Improper heading hierarchy`markdown## 1.1 Text Alternatives ### 1.1.1 Non-text Content (Level A) - [ ] All images have alt text - [ ] Decorative images have alt="" - [ ] Complex images have long descriptions - [ ] Icons with meaning have accessible names - [ ] CAPTCHAs have alternatives Check:
<!-- Good --> <img src="chart.png" alt="Sales increased 25% from Q1 to Q2" /> <img src="decorative-line.png" alt="" />
<!-- Bad --> <img src="chart.png" /> <img src="decorative-line.png" alt="decorative line" />
Check:
html<!-- Heading hierarchy --> <h1>Page Title</h1> <h2>Section</h2> <h3>Subsection</h3> <h2>Another Section</h2> <!-- Table headers --> <table> <thead> <tr> <th scope="col">Name</th> <th scope="col">Price</th> </tr> </thead> </table>
Tools: WebAIM Contrast Checker, axe DevTools
`### Operable (Principle 2)
Check:
javascript// Custom button must be keyboard accessible <div role="button" tabindex="0" onkeydown="if(event.key === 'Enter' || event.key === ' ') activate()">
css@media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } }
html<a href="#main" class="skip-link">Skip to main content</a> <main id="main">...</main>
html<!-- Bad --> <a href="report.pdf">Click here</a> <!-- Good --> <a href="report.pdf">Download Q4 Sales Report (PDF)</a>
css:focus { outline: 3px solid #005fcc; outline-offset: 2px; }
`### Understandable (Principle 3)
html<html lang="en">
html<p>The French word <span lang="fr">bonjour</span> means hello.</p>
html<input aria-describedby="email-error" aria-invalid="true" /> <span id="email-error" role="alert">Please enter valid email</span>
`### Robust (Principle 4)
html<!-- Accessible custom checkbox --> <div role="checkbox" aria-checked="false" tabindex="0" aria-labelledby="label"> </div> <span id="label">Accept terms</span>
html<div role="status" aria-live="polite">3 items added to cart</div> <div role="alert" aria-live="assertive">Error: Form submission failed</div>
`## Automated Testing
// axe-core integration const axe = require('axe-core');
async function runAccessibilityAudit(page) { await page.addScriptTag({ path: require.resolve('axe-core') });
const results = await page.evaluate(async () => { return await axe.run(document, { runOnly: { type: 'tag', values: 'wcag2a', 'wcag2aa', 'wcag21aa', 'wcag22aa'] } }); });
return { violations: results.violations, passes: results.passes, incomplete: results.incomplete }; }
// Playwright test example test('should have no accessibility violations', async ({ page }) => { await page.goto('/'); const results = await runAccessibilityAudit(page);
expect(results.violations).toHaveLength(0); });
`
npx @axe-core/cli https://example.com npx pa11y https://example.com lighthouse https://example.com --only-categories=accessibility
## Remediation Patterns
### Fix: Missing Form Labels
<!-- Before --> <input type="email" placeholder="Email" />
<!-- After: Option 1 - Visible label --> <label for="email">Email address</label> <input id="email" type="email" />
<!-- After: Option 2 - aria-label --> <input type="email" aria-label="Email address" />
<!-- After: Option 3 - aria-labelledby --> <span id="email-label">Email</span> <input type="email" aria-labelledby="email-label" />
### Fix: Insufficient Color Contrast
/ Before: 2.5:1 contrast / .text { color: #767676; }
/ After: 4.5:1 contrast / .text { color: #595959; }
/ Or add background / .text { color: #767676; background: #000; }
### Fix: Keyboard Navigation
// Make custom element keyboard accessible class AccessibleDropdown extends HTMLElement { connectedCallback() { this.setAttribute("tabindex", "0"); this.setAttribute("role", "combobox"); this.setAttribute("aria-expanded", "false");
this.addEventListener("keydown", (e) => { switch (e.key) { case "Enter": case " ": this.toggle(); e.preventDefault(); break; case "Escape": this.close(); break; case "ArrowDown": this.focusNext(); e.preventDefault(); break; case "ArrowUp": this.focusPrevious(); e.preventDefault(); break; } }); } }
## Best Practices
### Do's
- **Start early** - Accessibility from design phase
- **Test with real users** - Disabled users provide best feedback
- **Automate what you can** - 30-50% issues detectable
- **Use semantic HTML** - Reduces ARIA needs
- **Document patterns** - Build accessible component library
### Don'ts
- **Don't rely only on automated testing** - Manual testing required
- **Don't use ARIA as first solution** - Native HTML first
- **Don't hide focus outlines** - Keyboard users need them
- **Don't disable zoom** - Users need to resize
- **Don't use color alone** - Multiple indicators needed
## Resources
- [WCAG 2.2 Guidelines](https://www.w3.org/TR/WCAG22/)
- [WebAIM](https://webaim.org/)
- [A11y Project Checklist](https://www.a11yproject.com/checklist/)
- [axe DevTools](https://www.deque.com/axe/)| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-19 | pass→fail | 10,136 | 14,273 | +41% | 1 | 1 | 0% | 1,909 | 5,970 | +213% | 0 | 0 | — |
case-01 | fail→fail | 11,320 | 26,257 | +132% | 1 | 1 | 0% | 2,384 | 6,344 | +166% | 0 | 0 | — |
case-02 | pass→pass | 13,150 | 12,803 | -3% | 1 | 1 | 0% | 2,065 | 5,595 | +171% | 0 | 0 | — |
case-03 | pass→pass | 9,052 | 10,195 | +13% | 1 | 1 | 0% | 1,534 | 5,431 | +254% | 0 | 0 | — |
case-04 | pass→pass | 13,176 | 10,898 | -17% | 1 | 1 | 0% | 2,051 | 5,616 | +174% | 0 | 0 | — |
case-05 | pass→pass | 12,982 | 22,790 | +76% | 1 | 1 | 0% | 2,717 | 8,247 | +204% | 0 | 0 | — |
case-06 | pass→pass | 13,842 | 13,444 | -3% | 1 | 1 | 0% | 2,545 | 6,060 | +138% | 0 | 0 | — |
case-07 | pass→pass | 14,431 | 16,216 | +12% | 1 | 1 | 0% | 2,587 | 6,419 | +148% | 0 | 0 | — |
case-08 | pass→pass | 13,796 | 12,079 | -12% | 1 | 1 | 0% | 2,306 | 6,038 | +162% | 0 | 0 | — |
case-09 | pass→pass | 12,776 | 15,995 | +25% | 1 | 1 | 0% | 2,306 | 6,028 | +161% | 0 | 0 | — |
case-10 | pass→pass | 11,482 | 8,633 | -25% | 1 | 1 | 0% | 2,027 | 5,030 | +148% | 0 | 0 | — |
case-11 | pass→pass | 8,535 | 6,540 | -23% | 1 | 1 | 0% | 1,346 | 4,644 | +245% | 0 | 0 | — |
case-12 | pass→pass | 11,761 | 12,288 | +4% | 1 | 1 | 0% | 2,148 | 5,822 | +171% | 0 | 0 | — |
case-13 | pass→pass | 16,088 | 13,372 | -17% | 1 | 1 | 0% | 2,538 | 5,961 | +135% | 0 | 0 | — |
case-14 | fail→pass | 12,134 | 14,684 | +21% | 1 | 1 | 0% | 2,132 | 5,656 | +165% | 0 | 0 | — |
case-15 | pass→pass | 17,379 | 18,477 | +6% | 1 | 1 | 0% | 2,679 | 6,746 | +152% | 0 | 0 | — |
case-16 | pass→pass | 12,714 | 11,250 | -12% | 1 | 1 | 0% | 1,813 | 5,401 | +198% | 0 | 0 | — |
case-17 | pass→pass | 14,245 | 13,167 | -8% | 1 | 1 | 0% | 2,618 | 6,066 | +132% | 0 | 0 | — |
case-18 | pass→pass | 8,581 | 9,537 | +11% | 1 | 1 | 0% | 1,239 | 4,899 | +295% | 0 | 0 | — |
case-20 | pass→pass | 20,478 | 26,063 | +27% | 1 | 1 | 0% | 3,021 | 7,682 | +154% | 0 | 0 | — |
case-21 | pass→pass | 13,692 | 25,320 | +85% | 1 | 1 | 0% | 2,471 | 7,210 | +192% | 0 | 0 | — |
case-22 | pass→pass | 9,509 | 12,252 | +29% | 1 | 1 | 0% | 1,278 | 5,683 | +345% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of 0 percentage points is the difference between those two pass rates over the 22 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.