Web Accessibility Guide

WCAG 2.1 Principles (POUR)

PrincipleDescriptionKey Criteria
PerceivableInfo must be presentable to users in ways they can perceiveAlt text, captions, color contrast, text resize
OperableUI components and navigation must be operableKeyboard access, no seizure triggers, enough time
UnderstandableInformation and UI operation must be understandableReadable text, predictable behavior, error suggestions
RobustContent must be interpreted by assistive technologiesValid HTML, ARIA, name/role/value

Color Contrast Requirements

LevelNormal TextLarge Text (18pt+)
AA (minimum)4.5:13:1
AAA (enhanced)7:14.5:1

ARIA Roles Reference

<!-- Landmark roles --> <header role="banner"> <nav role="navigation" aria-label="Main navigation"> <main role="main"> <aside role="complementary"> <footer role="contentinfo"> <!-- Interactive roles --> <div role="button" tabindex="0" aria-pressed="false">Toggle</div> <div role="dialog" aria-modal="true" aria-labelledby="dialog-title"> <h2 id="dialog-title">Confirm Delete</h2> </div> <!-- Live regions --> <div role="alert" aria-live="assertive">Error: Form submission failed</div> <div aria-live="polite" aria-atomic="true">3 items in cart</div> <!-- Form accessibility --> <label for="email">Email address</label> <input id="email" type="email" aria-required="true" aria-describedby="email-hint"> <span id="email-hint">We'll never share your email</span>

Keyboard Navigation

// Focus management for modal dialogs function openModal(modal) { modal.removeAttribute('hidden'); const focusable = modal.querySelectorAll( 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])' ); const firstFocusable = focusable[0]; const lastFocusable = focusable[focusable.length - 1]; firstFocusable.focus(); // Trap focus in modal modal.addEventListener('keydown', (e) => { if (e.key === 'Tab') { if (e.shiftKey) { if (document.activeElement === firstFocusable) { e.preventDefault(); lastFocusable.focus(); } } else { if (document.activeElement === lastFocusable) { e.preventDefault(); firstFocusable.focus(); } } } if (e.key === 'Escape') closeModal(modal); }); }