HTML Accessibility
Master web accessibility principles to create inclusive experiences that work for all users, regardless of their abilities or assistive technologies.
Understanding Web Accessibility
Web accessibility ensures that websites and applications are usable by people with disabilities, including those who use assistive technologies like screen readers, keyboard navigation, or voice recognition software. It's not just a legal requirement in many jurisdictions—it's about creating inclusive digital experiences for everyone.
Accessible HTML provides the foundation for assistive technologies to understand and navigate your content effectively. When implemented correctly, accessibility features benefit all users, not just those with disabilities.
♿ Key Concept
Accessibility is about removing barriers and ensuring equal access to information and functionality for all users, regardless of their physical or cognitive abilities.
Semantic HTML for Accessibility
Using semantic HTML elements provides meaning and structure that assistive technologies can interpret:
<!-- Accessible page structure --> <!DOCTYPE html> <html lang="en"> <head> <title>Accessible Web Page - Company Name</title> <meta name="description" content="Clear description of page content"> </head> <body> <!-- Skip to main content link --> <a href="#main-content" class="skip-link">Skip to main content</a> <!-- Main header with navigation --> <header role="banner"> <h1>Website Name</h1> <nav role="navigation" aria-label="Main navigation"> <ul> <li><a href="/home" aria-current="page">Home</a></li> <li><a href="/about">About</a></li> <li><a href="/contact">Contact</a></li> </ul> </nav> </header> <!-- Main content area --> <main id="main-content" role="main"> <h1>Page Title</h1> <section aria-labelledby="section-heading"> <h2 id="section-heading">Section Title</h2> <p>Content that's properly structured for screen readers.</p> </section> </main> <!-- Sidebar with complementary content --> <aside role="complementary" aria-label="Related information"> <h2>Related Links</h2> <ul> <li><a href="/related-page">Related Topic</a></li> </ul> </aside> <!-- Footer --> <footer role="contentinfo"> <p>© 2025 Company Name. All rights reserved.</p> </footer> </body> </html>
- Skip links for keyboard navigation
- Proper heading hierarchy (h1, h2, h3...)
- Semantic HTML elements (nav, main, aside)
- ARIA labels and roles for clarity
- Language declarations
ARIA Attributes
ARIA (Accessible Rich Internet Applications) attributes provide additional semantic information:
<!-- ARIA roles --> <div role="button" tabindex="0" onclick="handleClick()"> Custom Button </div> <nav role="navigation" aria-label="Breadcrumb navigation"> <ol> <li><a href="/home">Home</a></li> <li><a href="/products">Products</a></li> <li aria-current="page">Current Product</li> </ol> </nav> <!-- ARIA states and properties --> <button aria-expanded="false" aria-controls="menu" onclick="toggleMenu()"> Menu </button> <ul id="menu" aria-hidden="true"> <li><a href="/option1">Option 1</a></li> <li><a href="/option2">Option 2</a></li> </ul> <!-- Live regions for dynamic content --> <div aria-live="polite" id="status-message"> <!-- Status updates will be announced --> </div> <div aria-live="assertive" id="error-message"> <!-- Critical updates will interrupt screen reader --> </div> <!-- Descriptions and labels --> <input type="password" id="password" aria-describedby="password-help"> <div id="password-help"> Password must be at least 8 characters long </div> <fieldset> <legend>Shipping Address</legend> <div role="group" aria-labelledby="name-group-label"> <span id="name-group-label">Full Name</span> <input type="text" aria-label="First name"> <input type="text" aria-label="Last name"> </div> </fieldset>
Accessible Forms
<!-- Comprehensive accessible form --> <form action="/submit" method="POST" novalidate> <fieldset> <legend>Personal Information</legend> <!-- Required field with proper labeling --> <div class="form-group"> <label for="full-name"> Full Name <span class="required" aria-label="required">*</span> </label> <input type="text" id="full-name" name="fullName" required aria-invalid="false" aria-describedby="name-error"> <div id="name-error" class="error-message" aria-hidden="true"> <!-- Error message appears here --> </div> </div> <!-- Email with validation --> <div class="form-group"> <label for="email">Email Address *</label> <input type="email" id="email" name="email" required autocomplete="email" aria-describedby="email-help email-error"> <div id="email-help" class="help-text"> We'll use this to send you updates </div> <div id="email-error" class="error-message" aria-hidden="true"> <!-- Error message appears here --> </div> </div> </fieldset> <fieldset> <legend>Preferences</legend> <!-- Radio buttons with proper grouping --> <fieldset> <legend>Preferred Contact Method</legend> <div class="radio-group"> <input type="radio" id="contact-email" name="contactMethod" value="email"> <label for="contact-email">Email</label> <input type="radio" id="contact-phone" name="contactMethod" value="phone"> <label for="contact-phone">Phone</label> </div> </fieldset> <!-- Checkboxes --> <div class="form-group"> <input type="checkbox" id="newsletter" name="newsletter"> <label for="newsletter"> Subscribe to newsletter </label> </div> </fieldset> <!-- Submit button --> <div class="form-actions"> <button type="submit">Submit Form</button> <button type="reset">Clear Form</button> </div> </form>
Images and Media Accessibility
<!-- Informative images --> <img src="chart-sales-2025.png" alt="Sales increased 40% from January to March 2025, reaching $150,000 in Q1"> <!-- Decorative images --> <img src="decorative-border.png" alt="" role="presentation"> <!-- Complex images with descriptions --> <figure> <img src="complex-diagram.png" alt="Process flow diagram" aria-describedby="diagram-description"> <figcaption id="diagram-description"> The diagram shows three stages: Input processing (blue box), Data validation (green box), and Output generation (orange box). Arrows connect each stage sequentially. </figcaption> </figure> <!-- Video with captions and descriptions --> <video controls aria-label="Tutorial: How to use the contact form"> <source src="tutorial.mp4" type="video/mp4"> <!-- Captions for hearing accessibility --> <track src="captions.vtt" kind="captions" srclang="en" label="English" default> <!-- Audio descriptions for visual accessibility --> <track src="descriptions.vtt" kind="descriptions" srclang="en" label="Audio descriptions"> <!-- Fallback content --> <p>This tutorial shows how to fill out our contact form. <a href="tutorial-transcript.html">Read the full transcript</a>. </p> </video>
Keyboard Navigation
<!-- Skip links for keyboard users --> <a href="#main-content" class="skip-link">Skip to main content</a> <a href="#navigation" class="skip-link">Skip to navigation</a> <!-- Custom interactive elements with keyboard support --> <div role="button" tabindex="0" onclick="handleClick()" onkeydown="handleKeydown(event)" aria-pressed="false"> Toggle Button </div> <!-- Dropdown menu with keyboard navigation --> <div class="dropdown"> <button type="button" aria-haspopup="true" aria-expanded="false" aria-controls="dropdown-menu" onclick="toggleDropdown()"> Options Menu </button> <ul id="dropdown-menu" role="menu" aria-hidden="true"> <li role="menuitem" tabindex="-1"> <a href="/option1">Option 1</a> </li> <li role="menuitem" tabindex="-1"> <a href="/option2">Option 2</a> </li> </ul> </div> <!-- Focus management JavaScript --> <script> function handleKeydown(event) { // Handle Enter and Space keys for custom buttons if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); handleClick(); } } function trapFocus(element) { const focusableElements = element.querySelectorAll( 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])' ); const firstElement = focusableElements[0]; const lastElement = focusableElements[focusableElements.length - 1]; element.addEventListener('keydown', (e) => { if (e.key === 'Tab') { if (e.shiftKey) { if (document.activeElement === firstElement) { e.preventDefault(); lastElement.focus(); } } else { if (document.activeElement === lastElement) { e.preventDefault(); firstElement.focus(); } } } }); } </script>
Testing and Validation
Automated Testing Tools
- WAVE (Web Accessibility Evaluation Tool)
- axe-core accessibility engine
- Lighthouse accessibility audit
- Pa11y command line tool
- AccessiBe accessibility checker
Manual Testing Methods
- Keyboard-only navigation
- Screen reader testing (NVDA, JAWS, VoiceOver)
- High contrast mode testing
- Zoom testing (up to 200%)
- Color blindness simulation
WCAG Guidelines Summary
Perceivable
- Provide text alternatives for images
- Offer captions for videos
- Ensure sufficient color contrast
- Make content adaptable to different presentations
Operable
- Make all functionality keyboard accessible
- Give users enough time to read content
- Don't use content that causes seizures
- Help users navigate and find content
Understandable
- Make text readable and understandable
- Make content appear and operate predictably
- Help users avoid and correct mistakes
- Use clear and simple language
Robust
- Maximize compatibility with assistive technologies
- Use valid, semantic HTML
- Ensure content works across different browsers
- Follow web standards
Best Practices
✅ Do
- Use semantic HTML elements appropriately
- Provide meaningful alt text for images
- Ensure proper heading hierarchy
- Test with keyboard navigation only
- Include focus indicators for interactive elements
- Use ARIA attributes correctly
❌ Avoid
- Relying solely on color to convey information
- Using placeholder text as labels
- Creating keyboard traps
- Removing focus indicators
- Using generic link text like "click here"
- Ignoring error message accessibility