From Accessibility Audit
Audits components, pages, and designs against WCAG 2.1 AA and outputs a prioritized remediation checklist with code-level fixes. Use this skill whenever the user asks to check accessibility, run a WCAG audit, review color contrast, validate keyboard navigation, check screen reader support, or says things like "is this accessible?", "audit this for a11y", "check this for WCAG", "does this pass accessibility?", "make this accessible", or "review this component for accessibility issues." Also triggers when the user shares a React component, HTML file, CSS token set, or design description and asks for feedback — if accessibility is even a secondary concern, use this skill. Works across HTML pages, React components, Eleventy sites, CSS design tokens, and design mockup descriptions.
How this skill is triggered — by the user, by Claude, or both
Slash command
/accessibility-audit:accessibility-auditThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Run a structured WCAG 2.1 AA audit on any frontend artifact — React component, HTML page,
Run a structured WCAG 2.1 AA audit on any frontend artifact — React component, HTML page, CSS token set, Eleventy site, or a design described in prose — and produce a prioritized remediation report. The goal is not a pass/fail score but a clear action list: what to fix, why it matters, and exactly how to fix it.
.jsx / .tsx files.njk / .html outputIf the user provides a file path, read it. If they provide a URL, fetch it. If they paste code inline, work from that. If they describe a design in words, audit the description.
Before running checks, establish:
Read the input file(s) in full before starting the audit. If reading from a URL, fetch and parse the HTML. For React, analyze JSX semantics — what renders in the DOM matters most.
Work through the four WCAG principles in order. Don't skip categories even if the user mentions only one concern — a contrast check often surfaces semantic issues and vice versa.
See references/wcag-criteria.md for the full criterion-by-criterion breakdown.
Quick category guide:
| Category | What you're checking |
|---|---|
| Perceivable | Contrast ratios, alt text, color independence, text spacing, non-text contrast |
| Operable | Keyboard access, focus order, focus visibility, skip links, touch targets |
| Understandable | Form labels, error messages, language attributes, input purpose |
| Robust | Semantic HTML, ARIA correctness, name/role/value on interactive elements |
When CSS color values are present (hex, rgb, hsl, or CSS custom property references), calculate
contrast ratios programmatically rather than estimating visually. Use the script at
scripts/contrast_check.py — pass it pairs of foreground/background values.
python scripts/contrast_check.py "#1a1a1a" "#ffffff"
# → 19.1:1 ✓ PASS (AA normal, AA large, AAA normal, AAA large)
python scripts/contrast_check.py "#7d94a2" "#1e2a31"
# → 4.9:1 ✓ PASS (AA normal, AA large)
python scripts/contrast_check.py "#666666" "#f5f5f5"
# → 5.7:1 ✓ PASS (AA normal)
For CSS custom properties, resolve them to their computed values first (look up the token definition file, or ask the user to provide the resolved value).
WCAG contrast thresholds:
Classify every finding by severity:
| Severity | Meaning |
|---|---|
| Critical | Blocks access entirely for some users (no keyboard access to interactive element, contrast below 3:1 for body text, missing required form label) |
| High | Significantly impairs experience (contrast 3:1–4.49:1 for body text, missing alt text on meaningful image, no visible focus indicator) |
| Medium | Partial barrier or WCAG violation without complete blockage (incorrect ARIA role, missing live region on dynamic update, no skip link on long page) |
| Low | Best practice not met, minor friction (missing lang attribute, overly generic aria-label, touch target slightly under 44px) |
Always produce the audit report in this structure:
## Accessibility Audit — [Component/File Name]
### Summary
[2–3 sentences on overall state. Be direct: "This component has two critical issues that block
keyboard users entirely, plus four contrast failures. Everything else is solid."]
| Severity | Count |
|----------|-------|
| Critical | N |
| High | N |
| Medium | N |
| Low | N |
---
### Findings
#### [CRIT-01] [Short issue name]
**Criterion:** WCAG X.X.X — [Criterion name]
**Location:** [File:line or component/element description]
**Issue:** [What's wrong and why it matters to real users]
**Fix:**
[Code example of the corrected version]
#### [HIGH-01] [Short issue name]
...
---
### Remediation Roadmap
**Do first (Critical):** [Ordered list — address these before shipping]
**Do next (High):** [Ordered list — address in same sprint if possible]
**Do soon (Medium):** [Ordered list — next sprint]
**Backlog (Low):** [Ordered list — good to have]
---
### Passes ✓
[List what's already correct — this matters, it tells the user what not to break]
Keep findings concrete. "Missing aria-label on icon button at line 42" is useful.
"Some ARIA issues exist" is not.
<img> elements have meaningful alt text; decorative images have alt=""aria-label or visually-hidden text<div>)<ul> / <ol> / <dl>, not <div> + <br><th> with scope, <caption> where helpfuloutline: none / outline: 0 not used without a custom focus style<html lang="en"> (or correct language) present<label> (via for/id or aria-labelledby)id attributes<button> used for actions, <a> used for navigationrole="button" on <div>/<span> only when truly necessary (and with tabindex="0" + keyboard handler)aria-hidden="true" on focusable elements)aria-live or role status/alertIcon-only button:
// ✗ No accessible name
<button onClick={onClose}><XIcon /></button>
// ✓ With aria-label
<button onClick={onClose} aria-label="Close dialog"><XIcon aria-hidden="true" /></button>
Visible focus indicator:
/* ✗ Kills focus visibility */
*:focus { outline: none; }
/* ✓ Custom indicator */
:focus-visible {
outline: 2px solid var(--color-focus);
outline-offset: 2px;
}
Color-not-only for errors:
// ✗ Color alone indicates error
<input className={errors.email ? 'border-red' : ''} />
// ✓ Icon + text + color
<input
aria-invalid={!!errors.email}
aria-describedby="email-error"
className={errors.email ? 'input--error' : ''}
/>
{errors.email && (
<span id="email-error" role="alert">
<ErrorIcon aria-hidden="true" /> {errors.email}
</span>
)}
Modal focus trap:
// Focus must stay inside modal while open
// On open: move focus to first focusable element inside
// On Escape: close and return focus to trigger
// Tab / Shift+Tab: cycle within modal only
Skip link:
<a href="#main" class="skip-link">Skip to main content</a>
<main id="main">...</main>
.skip-link {
position: absolute;
transform: translateY(-100%);
transition: transform 0.2s;
}
.skip-link:focus { transform: translateY(0); }
references/accessibility.mdWhen a fix requires adding or changing design tokens (e.g., a new --color-focus token for
focus rings), recommend the user run the design-tokens skill to integrate the change properly.
npx claudepluginhub adamj/productdesign-skills --plugin productdesign-skillsCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.