21 KiB
Accessibility Review
Performs comprehensive accessibility (a11y) audit focusing on WCAG compliance, screen reader compatibility, keyboard navigation, and inclusive design principles.
Parameters
Received from router: $ARGUMENTS (after removing 'accessibility' operation)
Expected format: scope:"review-scope" [depth:"quick|standard|deep"] [level:"A|AA|AAA"]
Workflow
1. Parse Parameters
Extract from $ARGUMENTS:
- scope: What to review (required) - components, pages, features
- depth: Review thoroughness (default: "standard")
- level: WCAG compliance level (default: "AA")
WCAG Compliance Levels
- Level A: Minimum accessibility (basic compliance)
- Level AA: Standard accessibility (recommended target)
- Level AAA: Enhanced accessibility (gold standard)
2. Gather Context
Identify UI Components:
# Find frontend components
find . -name "*.tsx" -o -name "*.jsx" -o -name "*.vue" -o -name "*.svelte" | head -20
# Check for accessibility tooling
cat package.json | grep -E "axe|pa11y|lighthouse|eslint-plugin-jsx-a11y"
# Look for ARIA usage
grep -r "aria-" --include="*.tsx" --include="*.jsx" --include="*.html" | head -20
# Check for role attributes
grep -r 'role=' --include="*.tsx" --include="*.jsx" --include="*.html" | head -20
3. Semantic HTML Review
Proper HTML Structure:
- Semantic HTML elements used (
<header>,<nav>,<main>,<footer>,<article>,<section>) - Headings in logical order (h1 → h2 → h3, no skipping)
- Lists use
<ul>,<ol>,<dl>appropriately - Tables use
<table>,<th>,<caption>properly - Forms use
<form>,<label>,<fieldset>,<legend> - Buttons use
<button>(not styled divs) - Links use
<a>with href attribute
Document Structure:
- Single
<h1>per page describing main content - Landmark regions defined (
<header>,<nav>,<main>,<aside>,<footer>) - Skip to main content link present
- Page has meaningful
<title> - Language attribute on
<html>element
Code Examples - Semantic HTML:
<!-- ❌ BAD: Non-semantic markup -->
<div class="header">
<div class="nav">
<div class="nav-item" onclick="navigate('/home')">Home</div>
<div class="nav-item" onclick="navigate('/about')">About</div>
</div>
</div>
<div class="main-content">
<div class="title">Welcome</div>
<div class="text">Content here</div>
</div>
<!-- ✅ GOOD: Semantic HTML -->
<header>
<nav aria-label="Main navigation">
<ul>
<li><a href="/home">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
</header>
<main>
<h1>Welcome</h1>
<p>Content here</p>
</main>
<!-- ❌ BAD: Incorrect heading hierarchy -->
<h1>Main Title</h1>
<h3>Subsection</h3> <!-- Skipped h2! -->
<h2>Another Section</h2> <!-- Wrong order! -->
<!-- ✅ GOOD: Logical heading order -->
<h1>Main Title</h1>
<h2>Section</h2>
<h3>Subsection</h3>
<h2>Another Section</h2>
<h3>Another Subsection</h3>
4. ARIA (Accessible Rich Internet Applications)
ARIA Attributes:
- ARIA used only when semantic HTML insufficient
- ARIA roles appropriate and correct
- ARIA properties accurately reflect state
- ARIA labels provide meaningful descriptions
- Dynamic content changes announced
Common ARIA Patterns:
- Buttons:
role="button"witharia-pressedfor toggles - Dialogs:
role="dialog",aria-modal="true",aria-labelledby - Tabs:
role="tablist",role="tab",role="tabpanel",aria-selected - Menus:
role="menu",role="menuitem",aria-haspopup - Alerts:
role="alert"oraria-live="assertive" - Loading:
aria-busy="true",aria-live="polite"
ARIA Best Practices:
- First rule of ARIA: Don't use ARIA (prefer semantic HTML)
- ARIA doesn't change behavior, only semantics
- ARIA attributes are valid and supported
- Required ARIA attributes present
- ARIA properties reflect current state
Code Examples - ARIA:
// ❌ BAD: Div as button without proper ARIA
<div onClick={handleClick} className="button">
Click me
</div>
// ✅ GOOD: Proper button element (no ARIA needed)
<button onClick={handleClick}>
Click me
</button>
// ✅ ACCEPTABLE: Div with complete button semantics
<div
role="button"
tabIndex={0}
onClick={handleClick}
onKeyDown={(e) => e.key === 'Enter' && handleClick()}
aria-label="Click me"
>
Click me
</div>
// ❌ BAD: No ARIA for custom dropdown
<div className="dropdown">
<div onClick={toggle}>Menu</div>
{isOpen && (
<div className="menu">
<div onClick={handleOption1}>Option 1</div>
<div onClick={handleOption2}>Option 2</div>
</div>
)}
</div>
// ✅ GOOD: Proper ARIA for dropdown
<div className="dropdown">
<button
aria-haspopup="true"
aria-expanded={isOpen}
onClick={toggle}
>
Menu
</button>
{isOpen && (
<ul role="menu">
<li role="menuitem">
<button onClick={handleOption1}>Option 1</button>
</li>
<li role="menuitem">
<button onClick={handleOption2}>Option 2</button>
</li>
</ul>
)}
</div>
// ❌ BAD: Loading state not announced
{isLoading && <div className="spinner">Loading...</div>}
// ✅ GOOD: Loading state announced to screen readers
{isLoading && (
<div role="status" aria-live="polite">
<span className="spinner" aria-hidden="true"></span>
<span className="sr-only">Loading...</span>
</div>
)}
5. Keyboard Navigation
Keyboard Accessibility:
- All interactive elements keyboard accessible
- Tab order is logical and follows visual order
- Focus indicators visible and clear
- No keyboard traps (can always tab away)
- Skip links for keyboard users
- Shortcuts don't conflict with browser/screen reader
- Custom controls have keyboard support
Keyboard Interactions:
- Enter/Space activates buttons and links
- Arrow keys navigate lists and menus
- Escape closes dialogs and menus
- Tab moves to next element
- Shift+Tab moves to previous element
- Home/End navigate to start/end
Focus Management:
- Focus visible (no
outline: nonewithout alternative) - Focus moved appropriately (dialogs, route changes)
- Focus restored when closing dialogs
- Focus not lost during dynamic updates
- Focus indicator meets contrast requirements (3:1)
Code Examples - Keyboard Navigation:
// ❌ BAD: No keyboard support for custom control
<div onClick={handleClick} className="custom-button">
Click me
</div>
// ✅ GOOD: Full keyboard support
<div
role="button"
tabIndex={0}
onClick={handleClick}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleClick();
}
}}
className="custom-button"
>
Click me
</div>
/* ❌ BAD: Removed focus indicator */
*:focus {
outline: none; /* Never do this without alternative! */
}
/* ✅ GOOD: Enhanced focus indicator */
*:focus {
outline: 2px solid #4A90E2;
outline-offset: 2px;
}
/* ✅ BETTER: Visible focus, hidden for mouse users */
*:focus-visible {
outline: 2px solid #4A90E2;
outline-offset: 2px;
}
// ❌ BAD: Dialog doesn't trap focus
function Dialog({ isOpen, onClose, children }) {
if (!isOpen) return null;
return (
<div className="dialog">
<button onClick={onClose}>Close</button>
{children}
</div>
);
}
// ✅ GOOD: Dialog with focus trap
function Dialog({ isOpen, onClose, children }) {
const dialogRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (isOpen) {
// Focus first focusable element
const firstFocusable = dialogRef.current?.querySelector(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
(firstFocusable as HTMLElement)?.focus();
}
}, [isOpen]);
if (!isOpen) return null;
return (
<div
ref={dialogRef}
role="dialog"
aria-modal="true"
onKeyDown={(e) => e.key === 'Escape' && onClose()}
>
<button onClick={onClose}>Close</button>
{children}
</div>
);
}
6. Screen Reader Compatibility
Screen Reader Considerations:
- All content accessible to screen readers
- Images have alt text (or marked decorative)
- Form inputs have associated labels
- Error messages announced
- Dynamic content changes announced
- Hidden content not read by screen readers
- Reading order matches visual order
Text Alternatives:
- Images have descriptive alt text
- Decorative images have
alt=""oraria-hidden="true" - Icon buttons have accessible names
- SVG graphics have
<title>and<desc> - Video/audio has captions and transcripts
- Complex images have long descriptions
Form Accessibility:
- Labels associated with inputs (
<label for="...">or wrapping) - Required fields indicated (
requiredoraria-required) - Error messages associated with fields (
aria-describedby) - Fieldsets group related inputs
- Form validation errors announced
- Help text associated with inputs
Code Examples - Screen Readers:
// ❌ BAD: No alt text
<img src="/logo.png" />
// ✅ GOOD: Descriptive alt text
<img src="/logo.png" alt="Company Logo" />
// ✅ GOOD: Decorative image
<img src="/decorative-border.png" alt="" />
<img src="/background.png" aria-hidden="true" />
// ❌ BAD: Icon button without accessible name
<button onClick={handleDelete}>
<TrashIcon />
</button>
// ✅ GOOD: Icon button with accessible name
<button onClick={handleDelete} aria-label="Delete item">
<TrashIcon aria-hidden="true" />
</button>
// ✅ ALSO GOOD: Visually hidden text
<button onClick={handleDelete}>
<TrashIcon aria-hidden="true" />
<span className="sr-only">Delete item</span>
</button>
// ❌ BAD: Form without labels
<input type="text" placeholder="Enter your name" />
// ✅ GOOD: Properly labeled form field
<label htmlFor="name">Name</label>
<input type="text" id="name" placeholder="Enter your name" />
// ✅ ALSO GOOD: Wrapping label
<label>
Name
<input type="text" placeholder="Enter your name" />
</label>
// ❌ BAD: Error not associated with field
<input type="email" id="email" />
{error && <div className="error">Invalid email</div>}
// ✅ GOOD: Error associated with field
<input
type="email"
id="email"
aria-invalid={!!error}
aria-describedby={error ? "email-error" : undefined}
/>
{error && (
<div id="email-error" className="error" role="alert">
Invalid email
</div>
)}
7. Color and Contrast
Color Contrast (WCAG AA):
- Normal text: 4.5:1 contrast ratio minimum
- Large text (18pt+ or 14pt+ bold): 3:1 minimum
- UI components: 3:1 contrast with adjacent colors
- Focus indicators: 3:1 contrast with background
- WCAG AAA: 7:1 for normal text, 4.5:1 for large text
Color Usage:
- Information not conveyed by color alone
- Color blind friendly palette
- Links distinguishable without color (underline, icon)
- Form validation errors not color-only
- Charts/graphs have patterns or labels
Code Examples - Color and Contrast:
// ❌ BAD: Color only to indicate error
<input
type="text"
style={{ borderColor: hasError ? 'red' : 'gray' }}
/>
// ✅ GOOD: Multiple indicators for error
<input
type="text"
aria-invalid={hasError}
aria-describedby={hasError ? "error-message" : undefined}
style={{
borderColor: hasError ? 'red' : 'gray',
borderWidth: hasError ? '2px' : '1px'
}}
/>
{hasError && (
<div id="error-message" role="alert">
<ErrorIcon aria-hidden="true" />
This field is required
</div>
)}
// ❌ BAD: Link only differentiated by color
<a href="/more" style={{ color: 'blue', textDecoration: 'none' }}>
Read more
</a>
// ✅ GOOD: Link with underline
<a href="/more" style={{ color: 'blue', textDecoration: 'underline' }}>
Read more
</a>
// ✅ ALSO GOOD: Link with icon
<a href="/more" style={{ color: 'blue' }}>
Read more <ArrowIcon aria-hidden="true" />
</a>
8. Responsive and Adaptive Design
Viewport and Zoom:
- Content adapts to viewport size
- Text can be resized to 200% without loss of functionality
- No horizontal scrolling at 320px width
- Touch targets at least 44x44 pixels (mobile)
- Pinch-to-zoom not disabled
Media Queries:
- Reduced motion preferences respected
- High contrast mode supported
- Dark mode accessible
- Print styles appropriate
Code Examples - Responsive:
/* ❌ BAD: Disables zoom */
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
/* ✅ GOOD: Allows zoom */
<meta name="viewport" content="width=device-width, initial-scale=1">
/* ✅ GOOD: Respect reduced motion preference */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
// ✅ GOOD: Touch targets sized appropriately
<button style={{
minWidth: '44px',
minHeight: '44px',
padding: '12px'
}}>
Click me
</button>
9. Multimedia Accessibility
Video Accessibility:
- Captions for all audio content
- Audio descriptions for visual content
- Transcript provided
- Controls keyboard accessible
- Auto-play disabled or user-controlled
Audio Accessibility:
- Transcripts provided
- Visual indicators for audio cues
- User can control volume and playback
Code Examples - Multimedia:
<!-- ✅ GOOD: Video with captions and transcript -->
<video controls>
<source src="video.mp4" type="video/mp4">
<track kind="captions" src="captions.vtt" srclang="en" label="English">
<track kind="descriptions" src="descriptions.vtt" srclang="en" label="English">
</video>
<details>
<summary>Video Transcript</summary>
<p>[Full transcript here]</p>
</details>
10. Automated Testing
Run Accessibility Audits:
# Lighthouse audit
npx lighthouse https://your-site.com --only-categories=accessibility --view
# axe-core testing
npm install --save-dev @axe-core/cli
npx axe https://your-site.com
# pa11y testing
npm install --save-dev pa11y
npx pa11y https://your-site.com
# ESLint accessibility plugin (React)
npm install --save-dev eslint-plugin-jsx-a11y
Manual Testing:
- Test with keyboard only (no mouse)
- Test with screen reader (NVDA, JAWS, VoiceOver)
- Test with browser zoom at 200%
- Test in high contrast mode
- Test with CSS disabled
- Test on mobile devices
Review Depth Implementation
Quick Depth (10-15 min):
- Semantic HTML check
- Alt text verification
- Form label check
- Keyboard navigation test
- Automated audit (Lighthouse)
Standard Depth (30-45 min):
- Complete WCAG AA checklist
- ARIA usage review
- Keyboard navigation thorough test
- Focus management review
- Color contrast check
- Screen reader spot checks
Deep Depth (60-90+ min):
- Complete WCAG AAA checklist
- Comprehensive screen reader testing
- Keyboard-only navigation of entire scope
- Color blindness simulation
- Reduced motion testing
- High contrast mode testing
- Mobile accessibility testing
- Automated testing suite
Output Format
# Accessibility Review: [Scope]
## Executive Summary
**Reviewed**: [What was reviewed]
**Depth**: [Quick|Standard|Deep]
**WCAG Level Target**: [A|AA|AAA]
**Accessibility Rating**: [Excellent|Good|Needs Work|Poor]
### Overall Assessment
**[Compliant|Partially Compliant|Non-Compliant] with WCAG [Level]**
[Brief explanation]
### Priority Actions
1. [Critical a11y issue 1]
2. [Critical a11y issue 2]
---
## Critical Issues 🚨
**[Must fix for basic accessibility]**
### [Issue 1 Title]
**File**: `path/to/component.tsx:42`
**WCAG Criterion**: [X.X.X - Criterion Name - Level A/AA/AAA]
**Issue**: [Description of accessibility barrier]
**Impact**: [How this affects users]
**Affected Users**: [Screen reader users, keyboard users, low vision users, etc.]
**Fix**:
```tsx
// Current (inaccessible)
[problematic code]
// Accessible implementation
[fixed code]
Testing: [How to verify the fix]
[Repeat for each critical issue]
High Priority Issues ⚠️
[Should fix for good accessibility]
[Similar format for high priority issues]
Medium Priority Issues ℹ️
[Consider fixing for enhanced accessibility]
[Similar format for medium priority issues]
Low Priority Issues 💡
[Nice to have for optimal accessibility]
[Similar format for low priority issues]
Accessibility Strengths ✅
- ✅ [Good practice 1 with examples]
- ✅ [Good practice 2 with examples]
- ✅ [Good practice 3 with examples]
WCAG 2.1 Compliance Summary
Level A (Minimum)
| Criterion | Status | Notes |
|---|---|---|
| 1.1.1 Non-text Content | ✅ / ⚠️ / ❌ | [Details] |
| 1.3.1 Info and Relationships | ✅ / ⚠️ / ❌ | [Details] |
| 1.3.2 Meaningful Sequence | ✅ / ⚠️ / ❌ | [Details] |
| 2.1.1 Keyboard | ✅ / ⚠️ / ❌ | [Details] |
| 2.1.2 No Keyboard Trap | ✅ / ⚠️ / ❌ | [Details] |
| 2.4.1 Bypass Blocks | ✅ / ⚠️ / ❌ | [Details] |
| 3.1.1 Language of Page | ✅ / ⚠️ / ❌ | [Details] |
| 4.1.1 Parsing | ✅ / ⚠️ / ❌ | [Details] |
| 4.1.2 Name, Role, Value | ✅ / ⚠️ / ❌ | [Details] |
Level AA (Recommended)
| Criterion | Status | Notes |
|---|---|---|
| 1.4.3 Contrast (Minimum) | ✅ / ⚠️ / ❌ | [Details] |
| 1.4.5 Images of Text | ✅ / ⚠️ / ❌ | [Details] |
| 2.4.5 Multiple Ways | ✅ / ⚠️ / ❌ | [Details] |
| 2.4.6 Headings and Labels | ✅ / ⚠️ / ❌ | [Details] |
| 2.4.7 Focus Visible | ✅ / ⚠️ / ❌ | [Details] |
| 3.1.2 Language of Parts | ✅ / ⚠️ / ❌ | [Details] |
| 3.2.3 Consistent Navigation | ✅ / ⚠️ / ❌ | [Details] |
| 3.3.3 Error Suggestion | ✅ / ⚠️ / ❌ | [Details] |
| 3.3.4 Error Prevention (Legal) | ✅ / ⚠️ / ❌ | [Details] |
Level AAA (Enhanced) - Optional
[If deep review includes AAA]
Detailed Accessibility Analysis
🏗️ Semantic HTML
Overall: [Excellent|Good|Needs Work]
Strengths:
- ✅ [Well-structured areas]
Issues:
- ⚠️ [Semantic HTML issues with file references]
Heading Structure:
h1: [Page title]
h2: [Section 1]
h3: [Subsection]
h2: [Section 2]
🎯 ARIA Usage
Overall: [Appropriate|Overused|Underused]
Proper ARIA:
- ✅ [Good ARIA usage examples]
ARIA Issues:
- ⚠️ [ARIA problems with file references]
⌨️ Keyboard Navigation
Overall: [Fully Accessible|Partially Accessible|Not Accessible]
Tab Order: [Logical|Needs Work]
Keyboard Support:
- ✅ [Components with full keyboard support]
- ⚠️ [Components with keyboard issues]
Focus Management: [Good|Needs Improvement]
📢 Screen Reader Compatibility
Tested with: [NVDA|JAWS|VoiceOver|Narrator]
Screen Reader Experience: [Excellent|Good|Poor]
Issues Found:
- ⚠️ [Screen reader issues]
Missing Alternatives:
- ⚠️ [Missing alt text, labels, etc.]
🎨 Color and Contrast
Contrast Ratio Analysis:
| Element | Foreground | Background | Ratio | WCAG AA | WCAG AAA |
|---|---|---|---|---|---|
| Body text | [Color] | [Color] | [X:1] | ✅ / ❌ | ✅ / ❌ |
| Links | [Color] | [Color] | [X:1] | ✅ / ❌ | ✅ / ❌ |
| Buttons | [Color] | [Color] | [X:1] | ✅ / ❌ | ✅ / ❌ |
Color Usage: [Accessible|Issues Found]
📱 Responsive and Mobile
Mobile Accessibility: [Excellent|Good|Needs Work]
Touch Target Sizes: [Adequate|Too Small]
Zoom Support: [Enabled|Disabled]
Testing Results
Automated Testing
Lighthouse Score: [X/100]
axe-core Results:
- Critical issues: [X]
- Serious issues: [X]
- Moderate issues: [X]
- Minor issues: [X]
Manual Testing
Keyboard-Only Navigation: [Pass|Fail] - [Details]
Screen Reader Testing: [Pass|Fail] - [Details]
Zoom to 200%: [Pass|Fail] - [Details]
High Contrast Mode: [Pass|Fail] - [Details]
Recommendations
Immediate (This Week)
- [Critical fix 1]
- [Critical fix 2]
Short-term (This Month)
- [High priority fix 1]
- [High priority fix 2]
Long-term (This Quarter)
- [Strategic improvement 1]
- [Strategic improvement 2]
Resources
Testing Tools:
- Lighthouse (Chrome DevTools)
- axe DevTools (browser extension)
- WAVE (browser extension)
- Screen reader (NVDA, JAWS, VoiceOver)
Guidelines:
- WCAG 2.1: https://www.w3.org/WAI/WCAG21/quickref/
- ARIA Authoring Practices: https://www.w3.org/WAI/ARIA/apg/
Review Metadata
- Reviewer: 10x Fullstack Engineer (Accessibility Focus)
- Review Date: [Date]
- WCAG Level: [A|AA|AAA]
- A11y Issues: Critical: X, High: X, Medium: X, Low: X
- Compliance Status: [Compliant|Partially Compliant|Non-Compliant]
## Agent Invocation
This operation MUST leverage the **10x-fullstack-engineer** agent with accessibility expertise.
## Best Practices
1. **Semantic HTML First**: Use native elements before ARIA
2. **Test with Real Users**: Automated tools catch ~30-40% of issues
3. **Keyboard First**: If keyboard works, most other things will follow
4. **Don't Assume**: Test with actual assistive technology
5. **Progressive Enhancement**: Start accessible, add features
6. **Inclusive Design**: Consider all users from the beginning