Audit web applications for WCAG accessibility compliance. Use when asked to run accessibility checks, identify common violations, and provide remediation guidance.
Install with Tessl CLI
npx tessl i github:warpdotdev/oz-skills --skill web-accessibility-audit87
Does it follow best practices?
If you maintain this skill, you can automatically optimize it using the tessl CLI to improve its score:
npx tessl skill review --optimize ./path/to/skillValidation for skill structure
Audit web applications for WCAG 2.0/2.1/2.2 compliance by identifying common violations and providing actionable remediation steps.
| Principle | Description |
|---|---|
| Perceivable | Content can be perceived through different senses |
| Operable | Interface can be operated by all users |
| Understandable | Content and interface are understandable |
| Robust | Content works with assistive technologies |
| Level | Requirement | Target |
|---|---|---|
| A | Minimum accessibility | Must pass |
| AA | Standard compliance | Should pass (legal requirement in many jurisdictions) |
| AAA | Enhanced accessibility | Nice to have |
Based on WebAIM Million (2021) research analyzing top 1M websites:
Low Color Contrast (WCAG 1.4.3) - 86.4% of sites
Missing/Inadequate Alt Text (WCAG 1.1.1) - 60.6% of sites
Missing Name, Role, or Value (WCAG 4.1.2)
Keyboard Navigation Failures (WCAG 2.1.1)
Unlabeled Form Controls (WCAG 1.3.1, 3.3.2) - 39.6% of sites
<label> or aria-labelMissing Language Attributes (WCAG 3.1.1) - 28.9% of sites
<html>Improper Heading Structure (WCAG 1.3.1, 2.4.6)
Empty Links or Poor Link Text (WCAG 2.4.4)
Missing/Improper Focus Indicators (WCAG 2.4.7)
Overuse/Misuse of ARIA (WCAG 4.1.2)
Inadequate Data Table Markup (WCAG 1.3.1)
<th> elementsMissing Media Captions (WCAG 1.2.1, 1.2.2)
Run ESLint (React/JSX projects):
npx eslint --ext .jsx,.tsx --no-ignore --format json . > .claude/skills/a11y-auditor/eslint-results.json 2>&1 || trueOr use helper script: .claude/skills/a11y-auditor/scripts/run-eslint.sh
Run Lighthouse (production/staging):
npx lighthouse https://example.com --only-categories=accessibility --output=json --output-path=./lighthouse-results.jsonCheck for axe-core integration:
grep -r "@axe-core\|axe-core" package.jsonUse grep patterns from references/grep-patterns.md to search for:
See references/grep-patterns.md for complete pattern list.
Group findings by severity using WCAG impact levels:
Critical (fix immediately):
Serious (fix before launch):
Moderate (fix soon):
Follow references/screen-reader-guide.md for:
<!-- ❌ Missing alt -->
<img src="chart.png">
<!-- ✅ Descriptive alt -->
<img src="chart.png" alt="Bar chart showing 40% increase in Q3 sales">
<!-- ✅ Decorative (empty alt) -->
<img src="decorative-border.png" alt="" role="presentation">/* ❌ Low contrast (2.5:1) */
.low-contrast {
color: #999;
background: #fff;
}
/* ✅ Sufficient contrast (7:1) */
.high-contrast {
color: #333;
background: #fff;
}Contrast requirements:
<video controls>
<source src="video.mp4" type="video/mp4">
<track kind="captions" src="captions.vtt" srclang="en" label="English" default>
</video>// ❌ Only click
element.addEventListener('click', handleAction);
// ✅ Click + keyboard
element.addEventListener('click', handleAction);
element.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleAction();
}
});/* ❌ Never remove focus */
*:focus { outline: none; }
/* ✅ Keyboard-only focus */
:focus-visible {
outline: 2px solid #005fcc;
outline-offset: 2px;
}<body>
<a href="#main-content" class="skip-link">Skip to main content</a>
<header><!-- navigation --></header>
<main id="main-content" tabindex="-1">
<!-- content -->
</main>
</body>.skip-link {
position: absolute;
top: -40px;
left: 0;
background: #000;
color: #fff;
padding: 8px 16px;
z-index: 100;
}
.skip-link:focus {
top: 0;
}@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}<!-- ❌ No language -->
<html>
<!-- ✅ Language specified -->
<html lang="en">
<!-- ✅ Language changes -->
<p>The French word for hello is <span lang="fr">bonjour</span>.</p><!-- ❌ No label -->
<input type="email" placeholder="Email">
<!-- ✅ Explicit label -->
<label for="email">Email address</label>
<input type="email" id="email" autocomplete="email">
<!-- ✅ With hint -->
<label for="password">Password</label>
<input type="password" id="password" aria-describedby="password-requirements">
<p id="password-requirements">
Must be at least 8 characters with one number.
</p><label for="email">Email</label>
<input type="email" id="email"
aria-invalid="true"
aria-describedby="email-error">
<p id="email-error" role="alert">
Please enter a valid email address.
</p><!-- ❌ Unnecessary ARIA -->
<button role="button">Submit</button>
<!-- ✅ Native HTML -->
<button>Submit</button>
<!-- ✅ ARIA when needed (custom tabs) -->
<div role="tablist" aria-label="Product information">
<button role="tab" aria-selected="true" aria-controls="panel-1">
Description
</button>
<button role="tab" aria-selected="false" aria-controls="panel-2" tabindex="-1">
Reviews
</button>
</div>
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">
<!-- content -->
</div><!-- Polite (waits for pause) -->
<div aria-live="polite" aria-atomic="true">
Status update
</div>
<!-- Assertive (interrupts) -->
<div role="alert" aria-live="assertive">
Error: Form submission failed
</div>.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}<button>
<svg aria-hidden="true"><!-- icon --></svg>
<span class="visually-hidden">Delete item</span>
</button>Generate reports structured as:
# Accessibility Audit Report
## Summary
- Total Issues: X
- Critical: X | Serious: X | Moderate: X | Minor: X
- WCAG Level: A, AA, or AAA
- Automated Coverage: ~57% (manual testing required)
## Critical Issues (Fix Immediately)
### 1. [Issue Name] - WCAG X.X.X
**Severity:** Critical
**Impact:** [Who is affected and how]
**Affected:** X elements
**Locations:**
- `path/to/file.tsx:123`
- `path/to/file.tsx:456`
**Problem:**
[Brief description]
**Fix:**
```tsx
// Before
<div onClick={handleClick}>Click me</div>
// After
<button onClick={handleClick}>Click me</button>Why: [Accessibility principle]
[Same format]
[Same format]
[Prioritized action items]
---
## Tools & Resources
### Development Tools
- **eslint-plugin-jsx-a11y** - React/JSX static analysis (~37 rules)
- **axe-core DevTools** - Browser extension for runtime testing
- **Lighthouse** - Built into Chrome DevTools
### Testing Tools
- **@axe-core/react** - Runtime accessibility testing
- **@axe-core/playwright** - E2E test integration
- **pa11y** - Automated command-line testing
### Manual Testing
- **WebAIM Contrast Checker** - https://webaim.org/resources/contrastchecker/
- **WAVE** - Browser extension for visual feedback
- **Screen readers** - NVDA (Windows), VoiceOver (macOS), JAWS
### Reference Docs
- `references/WCAG-criteria.md` - All WCAG 2.1 success criteria
- `references/ARIA-patterns.md` - Common ARIA patterns and examples
- `references/screen-reader-guide.md` - Testing commands and scenarios
- `references/grep-patterns.md` - Search patterns for code audits
### References
- [WebAIM Million](https://webaim.org/projects/million/) - Annual analysis of top 1M websites (violation statistics)
- [WCAG 2.1 Quick Reference](https://www.w3.org/WAI/WCAG21/quickref/) - Interactive WCAG guide
- [WAI-ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/) - Official ARIA patterns
- [Deque axe Rules](https://dequeuniversity.com/rules/axe/) - All axe-core rules explained
- [jsx-a11y Rules](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y#supported-rules) - ESLint accessibility rules
---
## Important Notes
- Automated tools catch 30-57% of issues; manual testing required
- Pages with ARIA average 41% more errors than without
- Always test with actual assistive technology when possible
- Focus on critical issues first (keyboard, screen readers, contrast)
- Document deliberate accessibility decisions
- Test on multiple browsers and devices
- Include users with disabilities in testing when possible
## Common Pitfalls to Avoid
1. Relying solely on automated testing
2. Using ARIA when native HTML suffices
3. Removing focus indicators
4. Using positive tabindex values
5. Color as only means of conveying information
6. Keyboard traps in modals/dialogs
7. Non-descriptive link text
8. Missing or incorrect heading hierarchy
9. Unlabeled form controls
10. Missing language attributes2ca570e
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.