원클릭으로
arib-check-a11y
Check | Accessibility audit - WCAG 2.1 AA compliance, color contrast, ARIA, keyboard navigation, screen reader
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Check | Accessibility audit - WCAG 2.1 AA compliance, color contrast, ARIA, keyboard navigation, screen reader
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Memory | Code-graph subsystem — a native lightweight IMPORT graph (which file imports which, god-node candidates) built with ripgrep/grep, so a large monorepo can be navigated by structure instead of re-grepping every session. build/refresh/query. Honest scope: structural import graph, NOT semantic (no call-graph/type resolution); no Graphify dependency. Loads ON DEMAND only (zero always-on tokens). ADR-034.
Dev | Over-engineering review — reads a diff/module and returns a delete-list of bloat (single-use abstractions, premature generalization, speculative config, dead options) WITHOUT stripping legitimate structure. Advisory: returns recommendations, never auto-deletes. The on-demand companion to the ponytail-lite tripwire hook. Authored natively (no Ponytail dependency). ADR-033.
Wave | Pre-wave requirement lock — Act 1 derives the requirements from the codebase + memory (an honest grill, not guesswork), Act 2 hands the locked plan to an independent model (Codex) to tear apart until sign-off. Produces waves/<id>/PLAN.md + PLAN-REVIEW-LOG.md. Auto-chained idempotently from /arib-wave-start. If Act 2 can't run (no Codex), the wave proceeds but HOLDS MERGE for a human. Absorbs grill-me-codex (ADR-032).
Wave | Start a multi-session delivery wave — branch, plan, parallel architect+planner
Engine | Command the engineering team to deliver a known goal — dispatches the engineer-manager to decompose → dispatch specialists (parallel where safe) → integrate → reconcile (verification-agent) → merge gate. Scales its own reach: runs inline for a bounded goal, escalates to a parallel Workflow for a broad one, and paces under /loop for a multi-turn campaign — only when it needs to. Use when a goal needs a coordinated TEAM, not one specialist. Sibling of /arib-engine: the engine DISCOVERS its own backlog; /arib-build EXECUTES a goal you hand it.
Stack | NestJS architecture & patterns reference — modules/providers/DI, DTO+validation, guards/interceptors/pipes/filters, config, async lifecycle, testing, and the security + performance pitfalls that bite at scale. Use when building or reviewing a NestJS backend. Composes with security-auditor (OWASP), database-guardian (TypeORM/Prisma migrations), and performance (N+1). Authored natively (the ECC graft was unsourceable; this is CCM's own, MIT).
| name | arib-check-a11y |
| argument-hint | [component|page] |
| description | Check | Accessibility audit - WCAG 2.1 AA compliance, color contrast, ARIA, keyboard navigation, screen reader |
Accessibility is not optional. WCAG 2.1 Level AA compliance is a legal requirement in most jurisdictions (US ADA, EU EN 301 549, UK Equality Act 2010) and an ethical obligation to serve all users. This audit identifies barriers that prevent people with disabilities from using your application, then provides concrete remediation steps.
The audit covers four WCAG principles:
Automated tools catch ~30% of issues. Manual testing with real screen readers and keyboards catches the rest.
Audit frontend code for WCAG 2.1/2.2 Level AA compliance. Checks semantic HTML, color contrast, ARIA usage, keyboard navigation, focus management, and screen reader compatibility. Produces an Accessibility Audit Report with remediation plan.
User types /arib-check-a11y [scope]
Examples:
/arib-check-a11y - Full frontend accessibility audit/arib-check-a11y src/components/LoginForm.tsx - Specific component/arib-check-a11y --page /dashboard - Specific page route/arib-check-a11y --contrast - Color contrast check only/arib-check-a11y --keyboard - Keyboard navigation audit only| Principle | Key Requirements | Severity if Missing |
|---|---|---|
| Perceivable | Images have alt text; text has 4.5:1 contrast; videos have captions | CRITICAL - Complete barrier for blind/low-vision users |
| Operable | Keyboard navigation works; focus visible; no keyboard traps | CRITICAL - Complete barrier for motor disability users |
| Understandable | Form inputs labeled; error messages clear; language set | HIGH - Confusing for all users, severe for cognitive disabilities |
| Robust | Semantic HTML; valid ARIA; works with screen readers | CRITICAL - Assistive tech depends on this |
Bad (missing alt):
<img src="user-avatar.png" />
Bad (uninformative alt):
<img src="product.png" alt="image" />
Good:
<img src="user-avatar.png" alt="Sarah Chen, Product Manager" />
<img src="chart.png" alt="Sales revenue grew 23% YoY from Q1 2025 to Q1 2026" />
Decorative images should have empty alt:
<img src="separator-line.png" alt="" aria-hidden="true" />
Contrast Ratio Requirements:
Bad (2.1:1 ratio - fails):
color: #777; /* light gray on white = too light */
background: white;
Good (4.5:1 ratio - passes):
color: #333; /* dark gray on white */
background: white;
Use tools: WebAIM Contrast Checker, Lighthouse, axe DevTools.
Bad:
<input type="email" placeholder="Enter email" />
Placeholder text disappears when typing; screen readers can't find the label.
Good:
<label for="email-input">Email Address</label>
<input id="email-input" type="email" aria-describedby="email-hint" />
<span id="email-hint">We'll never share your email</span>
Bad:
button:focus {
outline: none; /* Removes visible focus ring */
}
Good:
button:focus {
outline: 2px solid #0066cc;
outline-offset: 2px;
}
/* Or use modern focus-visible */
button:focus-visible {
outline: 3px solid #0066cc;
}
Bad:
<a href="/privacy">Click here</a> for our privacy policy
<a href="/docs">Read more</a>
Screen reader users hear "link click here" - not descriptive.
Good:
<a href="/privacy">Read our privacy policy</a>
<a href="/docs">View full documentation on database migrations</a>
Bad:
function Modal() {
return (
<div className="modal">
<button>Close</button>
<p>Content...</p>
{/* No focus trap - Escape key doesn't close */}
</div>
);
}
Good:
function Modal() {
const ref = useRef(null);
useEffect(() => {
// Trap focus inside modal
const handleKeyDown = (e) => {
if (e.key === 'Escape') closeModal();
};
document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}, []);
return (
<div className="modal" role="dialog" aria-labelledby="modal-title">
<h2 id="modal-title">Dialog Title</h2>
<button onClick={closeModal}>Close</button>
<p>Content...</p>
</div>
);
}
Automated tools find (30%):
Tools: axe DevTools, WAVE, Lighthouse, Pa11y
Manual testing finds (70%):
Always test with:
Read .claude/agents/accessibility.md and follow the 7-step protocol.
Find non-semantic elements (div-as-button), missing alt text, heading hierarchy issues, missing lang attribute, unlabeled form inputs.
Check for:
// BAD: Using div/span for interactive elements
<div onClick={handleClick}>Click me</div>
// GOOD: Using semantic elements
<button onClick={handleClick}>Click me</button>
// BAD: Non-sequential heading hierarchy
<h1>Title</h1>
<h3>Subsection</h3> <!-- Skip from h1 to h3 -->
// GOOD: Sequential headings
<h1>Title</h1>
<h2>Section</h2>
<h3>Subsection</h3>
// BAD: No heading on page
<div class="page-content">
Content here...
</div>
// GOOD: Semantic structure with headings
<main>
<h1>Page Title</h1>
<p>Content...</p>
</main>
Extract color values from CSS/Tailwind, calculate contrast ratios, flag failures against WCAG AA thresholds (4.5:1 normal text, 3:1 large text).
Contrast calculation:
Luminance = 0.2126 * R + 0.7152 * G + 0.0722 * B (each value 0-1)
Contrast Ratio = (L1 + 0.05) / (L2 + 0.05) where L1 > L2
Example: black (#000) on white (#fff)
= (1 + 0.05) / (0 + 0.05) = 21:1 ✓ Excellent
Example: #777 on white
= (0.4 + 0.05) / (1 + 0.05) ≈ 2.1:1 ✗ Fails AA
Check focus order, focus visibility, keyboard support for custom components, skip links, focus traps in modals.
Test protocol:
Validate ARIA usage (prefer native HTML), check live regions for notifications, verify expanded/selected states.
ARIA hierarchy (prefer top approach):
<!-- BEST: Use semantic HTML -->
<button aria-expanded="false" aria-controls="menu">Menu</button>
<ul id="menu" hidden>...</ul>
<!-- GOOD: Use ARIA when semantic HTML insufficient -->
<div role="button" tabindex="0" aria-pressed="false">Toggle</div>
<!-- AVOID: Overusing ARIA on semantic elements -->
<button role="button">Wrong - button already has role</button>
<!-- LIVE REGIONS for dynamic updates -->
<div role="status" aria-live="polite" id="status">Ready</div>
<script>
document.getElementById('status').textContent = 'File uploaded';
// Screen reader announces: "File uploaded"
</script>
Check prefers-reduced-motion, viewport zoom restrictions, reflow at 320px.
Reduce motion support:
/* Always allow animations, but respect user preference */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
Never restrict zoom:
<!-- BAD: Prevents zoom at 200% -->
<meta name="viewport" content="user-scalable=no, maximum-scale=1" />
<!-- GOOD: Allow full zoom -->
<meta name="viewport" content="width=device-width, initial-scale=1" />
Produce the Accessibility Audit Report with:
Report template:
# Accessibility Audit Report
**Date:** 2026-04-19
**Scope:** Full application
**Compliance:** 72% WCAG 2.1 AA
## Summary
- Perceivable: 85% (images, contrast)
- Operable: 60% (keyboard, focus)
- Understandable: 80% (labels, errors)
- Robust: 75% (semantic HTML, ARIA)
## Critical Issues (Must Fix Before Launch)
### 1. Missing Alt Text on Product Images
- **File:** src/components/ProductCard.tsx:45
- **Severity:** Level A - Blocks blind users
- **Count:** 12 occurrences
- **Fix:** Add descriptive alt text to all <img> tags
- **Effort:** 30 min
## High Issues (Must Fix This Sprint)
### 2. Low Color Contrast on Links
- **Affected:** All links in white text on light gray background
- **Ratio:** 3.2:1 (need 4.5:1)
- **Fix:** Change text color from #666 to #222
- **Effort:** 5 min
## Edge Cases & Testing Notes
### Edge Case: Dynamic Content Updates
When content updates after page load, always announce to screen reader:
```javascript
const announcement = document.createElement('div');
announcement.setAttribute('role', 'status');
announcement.setAttribute('aria-live', 'polite');
announcement.textContent = 'New messages loaded';
document.body.appendChild(announcement);
Don't build custom selects - they break accessibility. If you must:
function CustomSelect({ options, onChange }) {
const [open, setOpen] = useState(false);
return (
<div>
<label htmlFor="custom-select">Choose option</label>
<button
id="custom-select"
aria-haspopup="listbox"
aria-expanded={open}
onClick={() => setOpen(!open)}
>
{selectedOption}
</button>
{open && (
<ul role="listbox">
{options.map(opt => (
<li
key={opt}
role="option"
aria-selected={opt === selectedOption}
onClick={() => {
onChange(opt);
setOpen(false);
}}
>
{opt}
</li>
))}
</ul>
)}
</div>
);
}
<!-- GOOD: Proper table structure for screen readers -->
<table>
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Email</th>
<th scope="col">Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>Alice</td>
<td>alice@example.com</td>
<td>Active</td>
</tr>
</tbody>
</table>
| Mistake | Why It Fails | Fix |
|---|---|---|
<img alt="image"> | Empty/meaningless alt | <img alt="Team photo at 2026 conference"> |
<button style="cursor: pointer"> with no outline | Focus invisible | Add :focus { outline: 2px solid... } |
<label>Email</label><input> (unassociated) | Screen reader can't link them | Use <label for="id"> + <input id="id"> |
onclick handlers on <div> | Not keyboard accessible | Use <button> instead |
| Images in CSS backgrounds | Can't have alt text | Use HTML <img> or add aria-label to container |
| Color alone to communicate state | Colorblind users miss it | Add text/icon + color |
| "Click here" links | Not descriptive | "Download PDF report" |
prefers-reduced-motion ignored | Triggers motion sickness/seizures | Respect @media query |