with one click
a11y
Accessibility audit and patterns - WCAG 2.1 AA compliance for React apps.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
Accessibility audit and patterns - WCAG 2.1 AA compliance for React apps.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
Show token / tool usage stats from the local telemetry log. Use when you want to know "which tools am I burning context on", "which skills are expensive", or "was yesterday's session mostly Read/Grep or actually productive".
Parallel quality audit with 7 specialized agents (Opus). Finds bugs, violations, and quality issues. Use audit for fixes, brainstorm for features.
Manage environment variables with Doppler — auto-install CLI, login, link projects, wrap commands with `doppler run`. Replaces scattered .env files with a hub/spoke architecture.
Scaffolds new projects or onboards existing ones. Detects stack, creates monorepo/single-app, configures strict tooling. Use for greenfield or first-time setup.
Archives completed stories from prd.json to reduce token usage.
Autonomous task execution with testing and security. Works through all tasks without stopping.
| name | a11y |
| description | Accessibility audit and patterns - WCAG 2.1 AA compliance for React apps. |
| triggers | ["a11y","accessibility","wcag","screen reader"] |
| allowed-tools | Bash, Read, Grep, Glob |
| model | opus |
| user-invocable | true |
# Automated checks (catches ~30% of issues)
npx axe-core-cli http://localhost:3000
# Or in browser console
# Install axe DevTools extension → run scan
Automated tools catch 30%. Manual testing catches the rest.
// ❌ Bad - click-only interaction
<div onClick={handleClick}>Click me</div>
// ✅ Good - keyboard accessible
<button onClick={handleClick}>Click me</button>
// ✅ Good - custom element with keyboard support
<div
role="button"
tabIndex={0}
onClick={handleClick}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') handleClick()
}}
>
Click me
</div>
Test: Tab through entire page. Can you reach and activate everything?
// Dialog: trap focus inside, return on close
<Dialog onOpenChange={(open) => {
if (!open) triggerRef.current?.focus()
}}>
// Page navigation: focus main content
useEffect(() => {
document.getElementById('main-content')?.focus()
}, [pathname])
// Skip link (first element in body)
<a href="#main-content" className="sr-only focus:not-sr-only">
Skip to main content
</a>
| Element | Minimum Ratio | Enhanced |
|---|---|---|
| Normal text | 4.5:1 | 7:1 |
| Large text (18px+) | 3:1 | 4.5:1 |
| UI components | 3:1 | - |
// ❌ Bad - low contrast
<p className="text-gray-400">Light text on white</p>
// ✅ Good - use semantic tokens with proper contrast
<p className="text-muted-foreground">Accessible muted text</p>
// ❌ Bad
<img src="/artist.jpg" />
// ✅ Good - descriptive alt
<img src="/artist.jpg" alt="Artist profile photo of Luna performing on stage" />
// ✅ Good - decorative (skip by screen reader)
<img src="/divider.svg" alt="" role="presentation" />
// Audio/Video: always provide captions
<audio controls aria-label="Song preview: Midnight Drive">
// ❌ Bad - no label
<input placeholder="Email" />
// ✅ Good - associated label
<label htmlFor="email">Email</label>
<input id="email" type="email" aria-required="true" />
// ✅ Good - error messaging
<input
id="email"
aria-invalid={!!errors.email}
aria-describedby={errors.email ? "email-error" : undefined}
/>
{errors.email && (
<p id="email-error" role="alert" className="text-destructive text-sm">
{errors.email.message}
</p>
)}
// Live regions for dynamic content
<div aria-live="polite" aria-atomic="true">
{status === 'generating' && 'Generating your song...'}
</div>
// Loading states
<button disabled={loading} aria-busy={loading}>
{loading ? 'Saving...' : 'Save'}
</button>
// Custom components
<div role="tablist" aria-label="Song sections">
<button role="tab" aria-selected={active === 'lyrics'}>Lyrics</button>
<button role="tab" aria-selected={active === 'details'}>Details</button>
</div>
// ❌ Bad - div soup
<div class="header"><div class="nav">...</div></div>
// ✅ Good - semantic landmarks
<header><nav aria-label="Main">...</nav></header>
<main id="main-content">
<section aria-labelledby="songs-heading">
<h2 id="songs-heading">Your Songs</h2>
</section>
</main>
<footer>...</footer>
Accessibility Audit (WCAG 2.1 AA)
──────────────────────────────────
Keyboard Navigation: ✅ All interactive elements reachable
Focus Management: ⚠️ Dialog doesn't trap focus
Color Contrast: ✅ All text meets 4.5:1
Images: ⚠️ 3 images missing alt text
Forms: ✅ All inputs labeled
ARIA: ✅ Live regions for loading states
Semantic HTML: ⚠️ Missing landmark roles
Score: 78/100
Critical: 0 | High: 1 | Medium: 2 | Low: 1
| Skill | How It Integrates |
|---|---|
audit | A11y agent uses these patterns |
standards | A11y is part of "all UI states handled" |
design | New UI must meet contrast + keyboard requirements |
review | Flag a11y regressions |