|
| 1 | +# Agentic Code Standards - Accessibility-Focused Profile |
| 2 | + |
| 3 | +# Version: 1.0.0 |
| 4 | + |
| 5 | +# Profile: WCAG 2.1+ priority - accessibility first |
| 6 | + |
| 7 | +## Mission |
| 8 | + |
| 9 | +Every user, regardless of ability, deserves equal access to your application. This profile prioritizes accessibility above all else while maintaining code quality. |
| 10 | + |
| 11 | +## WCAG 2.1 Level AA Compliance (Minimum) |
| 12 | + |
| 13 | +### Perceivable - Everyone Can "See" This |
| 14 | + |
| 15 | +#### Text Alternatives |
| 16 | + |
| 17 | +- ALL images MUST have alt text (or alt="" if decorative) |
| 18 | +- Complex images (charts, diagrams) MUST have detailed descriptions |
| 19 | +- Icons with meaning MUST have accessible labels |
| 20 | +- Decorative images MUST have empty alt attributes |
| 21 | + |
| 22 | +#### Multimedia |
| 23 | + |
| 24 | +- ALL videos MUST have captions |
| 25 | +- ALL audio content MUST have transcripts |
| 26 | +- Captions MUST be accurate and synchronized |
| 27 | +- Audio descriptions MUST be provided for visual-only content |
| 28 | + |
| 29 | +#### Adaptable Content |
| 30 | + |
| 31 | +- Use semantic HTML (nav, main, header, footer, article, section) |
| 32 | +- Proper heading hierarchy (h1 → h2 → h3, no skipping) |
| 33 | +- Meaningful landmarks for screen reader navigation |
| 34 | +- Reading order MUST match visual order |
| 35 | +- Content MUST be understandable when CSS is disabled |
| 36 | + |
| 37 | +#### Distinguishable |
| 38 | + |
| 39 | +- **Color Contrast Requirements:** |
| 40 | + - Normal text: 4.5:1 minimum (7:1 for AAA) |
| 41 | + - Large text (18pt+ or 14pt+ bold): 3:1 minimum |
| 42 | + - UI components and graphics: 3:1 minimum |
| 43 | + - Use Color Contrast Analyzer tool |
| 44 | +- NEVER use color alone to convey information |
| 45 | +- Add patterns, icons, or text in addition to color |
| 46 | +- Audio MUST have option to pause/stop |
| 47 | +- No auto-playing audio >3 seconds |
| 48 | + |
| 49 | +### Operable - Everyone Can Use This |
| 50 | + |
| 51 | +#### Keyboard Accessible |
| 52 | + |
| 53 | +- ALL functionality MUST work with keyboard only |
| 54 | +- Visible focus indicators REQUIRED (2px outline minimum) |
| 55 | +- Focus order MUST be logical and intuitive |
| 56 | +- NO keyboard traps (user can always escape) |
| 57 | +- NEVER use `tabIndex > 0` |
| 58 | +- Support standard keys: Tab, Enter, Space, Escape, Arrow keys |
| 59 | + |
| 60 | +#### Enough Time |
| 61 | + |
| 62 | +- NO time limits unless: |
| 63 | + - User can turn off the limit |
| 64 | + - User can adjust limit to 10x default |
| 65 | + - User is warned and given 20 seconds to extend |
| 66 | +- Provide pause/stop for moving content |
| 67 | +- Auto-updates MUST have controls |
| 68 | + |
| 69 | +#### Seizures and Physical Reactions |
| 70 | + |
| 71 | +- NOTHING flashes more than 3 times per second |
| 72 | +- Avoid large flashing areas |
| 73 | +- Provide warnings for flashing content |
| 74 | +- Animation MUST respect `prefers-reduced-motion` |
| 75 | + |
| 76 | +#### Navigable |
| 77 | + |
| 78 | +- Skip navigation links for keyboard users |
| 79 | +- Descriptive page titles |
| 80 | +- Meaningful link text (avoid "click here", "read more") |
| 81 | +- Multiple ways to find pages (search, sitemap, navigation) |
| 82 | +- Clear focus indicators |
| 83 | +- Breadcrumbs for complex navigation |
| 84 | + |
| 85 | +### Understandable - Everyone Can Comprehend This |
| 86 | + |
| 87 | +#### Readable |
| 88 | + |
| 89 | +- Set language of page: `<html lang="en">` |
| 90 | +- Set language of parts: `<span lang="es">Hola</span>` |
| 91 | +- Use plain language (8th grade reading level or below) |
| 92 | +- Define unusual words and abbreviations |
| 93 | +- Expansion available for abbreviations |
| 94 | + |
| 95 | +#### Predictable |
| 96 | + |
| 97 | +- Consistent navigation across pages |
| 98 | +- Consistent identification (icons, buttons) |
| 99 | +- NO automatic context changes on focus |
| 100 | +- NO automatic context changes on input (without warning) |
| 101 | +- Forms submit ONLY on explicit action |
| 102 | + |
| 103 | +#### Input Assistance |
| 104 | + |
| 105 | +- Labels for ALL form inputs |
| 106 | +- Clear error messages with suggestions |
| 107 | +- Error prevention for legal/financial/data transactions |
| 108 | +- Confirmation pages for submissions |
| 109 | +- Ability to review and correct before final submission |
| 110 | + |
| 111 | +### Robust - All Devices Can Use This |
| 112 | + |
| 113 | +#### Compatible |
| 114 | + |
| 115 | +- Valid HTML (pass W3C validator) |
| 116 | +- Proper ARIA attributes when needed |
| 117 | +- Name, role, value for all UI components |
| 118 | +- Status messages programmatically determinable |
| 119 | +- Works with assistive technologies |
| 120 | +- Test with NVDA, JAWS, VoiceOver, TalkBack |
| 121 | + |
| 122 | +## ARIA - Use Carefully |
| 123 | + |
| 124 | +### When to Use ARIA |
| 125 | + |
| 126 | +- Only when semantic HTML isn't sufficient |
| 127 | +- Custom widgets (tabs, accordions, modals) |
| 128 | +- Live regions for dynamic content |
| 129 | +- Relationship indicators |
| 130 | + |
| 131 | +### ARIA Rules |
| 132 | + |
| 133 | +1. First Rule of ARIA: Don't use ARIA (use semantic HTML first) |
| 134 | +2. Never change semantic meaning |
| 135 | +3. All interactive ARIA controls must be keyboard accessible |
| 136 | +4. Don't hide visible and actionable elements from screen readers |
| 137 | +5. All interactive elements must have an accessible name |
| 138 | + |
| 139 | +### Common ARIA Patterns |
| 140 | + |
| 141 | +- `role="button"` - Only if you can't use `<button>` |
| 142 | +- `aria-label` - Provide name when visual label isn't present |
| 143 | +- `aria-labelledby` - Associate existing label |
| 144 | +- `aria-describedby` - Additional description |
| 145 | +- `aria-live` - Announce dynamic content changes |
| 146 | +- `aria-expanded` - State of collapsible content |
| 147 | +- `aria-hidden="true"` - Hide decorative elements |
| 148 | + |
| 149 | +## Forms Accessibility |
| 150 | + |
| 151 | +### Form Field Requirements |
| 152 | + |
| 153 | +- ALL inputs MUST have associated `<label>` elements |
| 154 | +- Use `<label for="inputId">` or wrap input |
| 155 | +- Required fields MUST be indicated (not just with color/asterisk) |
| 156 | +- Error messages MUST be programmatically associated |
| 157 | +- Validation MUST happen on blur or submit, not on input |
| 158 | + |
| 159 | +### Form Patterns |
| 160 | + |
| 161 | +```javascript |
| 162 | +// Good: Associated label |
| 163 | +<label htmlFor="email">Email Address *</label> |
| 164 | +<input id="email" type="email" required aria-describedby="email-help" /> |
| 165 | +<small id="email-help">We'll never share your email</small> |
| 166 | + |
| 167 | +// Good: Error handling |
| 168 | +<input |
| 169 | + id="email" |
| 170 | + type="email" |
| 171 | + aria-invalid={hasError} |
| 172 | + aria-describedby="email-error" |
| 173 | +/> |
| 174 | +{hasError && <p id="email-error" role="alert">Please enter a valid email</p>} |
| 175 | +``` |
| 176 | + |
| 177 | +### Form Validation |
| 178 | + |
| 179 | +- Inline validation on blur |
| 180 | +- Clear error messages with solutions |
| 181 | +- Error summary at top of form |
| 182 | +- Focus management to first error |
| 183 | +- Success confirmation |
| 184 | + |
| 185 | +## React Accessibility |
| 186 | + |
| 187 | +### Component Requirements |
| 188 | + |
| 189 | +- Semantic HTML elements preferred over divs |
| 190 | +- Button elements for actions, links for navigation |
| 191 | +- Proper heading hierarchy maintained |
| 192 | +- Focus management in modals and dynamic content |
| 193 | +- Announce dynamic content with aria-live |
| 194 | + |
| 195 | +### Focus Management |
| 196 | + |
| 197 | +```javascript |
| 198 | +// Good: Focus management in modal |
| 199 | +useEffect(() => { |
| 200 | + if (isOpen) { |
| 201 | + modalRef.current?.focus(); |
| 202 | + } |
| 203 | +}, [isOpen]); |
| 204 | + |
| 205 | +// Good: Trap focus in modal |
| 206 | +const handleKeyDown = (e) => { |
| 207 | + if (e.key === "Escape") closeModal(); |
| 208 | + if (e.key === "Tab") trapFocus(e); |
| 209 | +}; |
| 210 | +``` |
| 211 | + |
| 212 | +### Live Regions |
| 213 | + |
| 214 | +```javascript |
| 215 | +// Good: Announce loading state |
| 216 | +<div role="status" aria-live="polite" aria-atomic="true"> |
| 217 | + {isLoading ? "Loading..." : `${results.length} results found`} |
| 218 | +</div> |
| 219 | +``` |
| 220 | + |
| 221 | +## Testing Requirements |
| 222 | + |
| 223 | +### Automated Testing |
| 224 | + |
| 225 | +- axe-core or jest-axe in unit tests |
| 226 | +- Lighthouse accessibility audit (score 100) |
| 227 | +- pa11y or similar in CI/CD |
| 228 | +- ESLint jsx-a11y plugin enabled |
| 229 | + |
| 230 | +### Manual Testing |
| 231 | + |
| 232 | +1. Keyboard navigation (Tab, Enter, Space, Escape) |
| 233 | +2. Screen reader testing: |
| 234 | + - NVDA (Windows - free) |
| 235 | + - JAWS (Windows - paid) |
| 236 | + - VoiceOver (Mac/iOS - built-in) |
| 237 | + - TalkBack (Android - built-in) |
| 238 | +3. Color contrast analyzer |
| 239 | +4. Zoom to 200% (text must remain readable) |
| 240 | +5. Browser with CSS disabled |
| 241 | +6. prefers-reduced-motion testing |
| 242 | + |
| 243 | +### Testing Checklist |
| 244 | + |
| 245 | +- [ ] All functionality keyboard accessible |
| 246 | +- [ ] Visible focus indicators |
| 247 | +- [ ] Logical focus order |
| 248 | +- [ ] Screen reader announces content correctly |
| 249 | +- [ ] Color contrast passes |
| 250 | +- [ ] Alt text for images |
| 251 | +- [ ] Form labels associated |
| 252 | +- [ ] Error messages clear |
| 253 | +- [ ] No automatic timeouts |
| 254 | +- [ ] No flashing content |
| 255 | +- [ ] Semantic HTML used |
| 256 | +- [ ] Zoom to 200% works |
| 257 | + |
| 258 | +## Code Examples |
| 259 | + |
| 260 | +### Accessible Button |
| 261 | + |
| 262 | +```javascript |
| 263 | +// Good: Semantic button |
| 264 | +<button onClick={handleClick} aria-pressed={isPressed}> |
| 265 | + Toggle {isPressed ? 'On' : 'Off'} |
| 266 | +</button> |
| 267 | + |
| 268 | +// Bad: Div as button |
| 269 | +<div onClick={handleClick}>Click me</div> |
| 270 | +``` |
| 271 | + |
| 272 | +### Accessible Modal |
| 273 | + |
| 274 | +```javascript |
| 275 | +const Modal = ({ isOpen, onClose, children }) => { |
| 276 | + const modalRef = useRef(); |
| 277 | + |
| 278 | + useEffect(() => { |
| 279 | + if (isOpen) { |
| 280 | + modalRef.current?.focus(); |
| 281 | + } |
| 282 | + }, [isOpen]); |
| 283 | + |
| 284 | + return ( |
| 285 | + <div |
| 286 | + role="dialog" |
| 287 | + aria-modal="true" |
| 288 | + aria-labelledby="modal-title" |
| 289 | + ref={modalRef} |
| 290 | + tabIndex={-1} |
| 291 | + onKeyDown={(e) => e.key === "Escape" && onClose()} |
| 292 | + > |
| 293 | + <h2 id="modal-title">Modal Title</h2> |
| 294 | + {children} |
| 295 | + <button onClick={onClose}>Close</button> |
| 296 | + </div> |
| 297 | + ); |
| 298 | +}; |
| 299 | +``` |
| 300 | + |
| 301 | +### Accessible Navigation |
| 302 | + |
| 303 | +```javascript |
| 304 | +<nav aria-label="Main navigation"> |
| 305 | + <ul> |
| 306 | + <li> |
| 307 | + <a href="/" aria-current={isHome && "page"}> |
| 308 | + Home |
| 309 | + </a> |
| 310 | + </li> |
| 311 | + <li> |
| 312 | + <a href="/about">About</a> |
| 313 | + </li> |
| 314 | + <li> |
| 315 | + <a href="/contact">Contact</a> |
| 316 | + </li> |
| 317 | + </ul> |
| 318 | +</nav> |
| 319 | +``` |
| 320 | + |
| 321 | +## Resources |
| 322 | + |
| 323 | +### Guidelines |
| 324 | + |
| 325 | +- WCAG 2.1: https://www.w3.org/WAI/WCAG21/quickref/ |
| 326 | +- ARIA Authoring Practices: https://www.w3.org/WAI/ARIA/apg/ |
| 327 | +- WebAIM: https://webaim.org/ |
| 328 | + |
| 329 | +### Testing Tools |
| 330 | + |
| 331 | +- axe DevTools: https://www.deque.com/axe/devtools/ |
| 332 | +- WAVE: https://wave.webaim.org/ |
| 333 | +- Color Contrast Analyzer: https://www.tpgi.com/color-contrast-checker/ |
| 334 | + |
| 335 | +### Screen Readers |
| 336 | + |
| 337 | +- NVDA (free): https://www.nvaccess.org/ |
| 338 | +- VoiceOver: Built into Mac/iOS |
| 339 | +- TalkBack: Built into Android |
| 340 | + |
| 341 | +Accessibility is not optional. |
| 342 | +Full documentation: https://github.com/nitishkaf/agentic-code-standards |
0 commit comments