Tutorial
May 30, 2026
19 min read

Web Accessibility (a11y) 2026: Build Inclusive Applications

Master web accessibility with ARIA, keyboard navigation, semantic HTML, and WCAG compliance. Build inclusive web applications that work for everyone.

Rehman Farouq

Full Stack Developer | Accessibility Expert

Introduction to Web Accessibility

Web accessibility ensures that everyone, including people with disabilities, can use your website. It's not just about compliance—it's about creating inclusive experiences for all users.

This guide covers essential accessibility concepts including semantic HTML, ARIA attributes, keyboard navigation, and WCAG compliance.

Semantic HTML

Semantic HTML provides meaning to your content, making it accessible to screen readers and assistive technologies.

<!-- Bad: Non-semantic div soup -->
<div class="header">
  <div class="nav">
    <div class="link">Home</div>
  </div>
</div>

<!-- Good: Semantic HTML -->
<header>
  <nav>
    <ul>
      <li><a href="/">Home</a></li>
    </ul>
  </nav>
</header>

<!-- Semantic structure -->
<header>Page header</header>
<nav>Navigation links</nav>
<main>Main content</main>
<article>Self-contained content</article>
<section>Thematic grouping</article>
<aside>Related content</aside>
<footer>Page footer</footer>

<!-- Headings hierarchy -->
<h1>Main page title</h1>
  <h2>Section title</h2>
    <h3>Subsection title</h3>
  <h2>Another section</h2>

<!-- Form accessibility -->
<form>
  <label for="email">Email address</label>
  <input 
    type="email" 
    id="email" 
    name="email"
    required
    aria-describedby="email-help"
  />
  <small id="email-help">We'll never share your email.</small>
  
  <button type="submit">Subscribe</button>
</form>

<!-- Tables -->
<table>
  <caption>Monthly sales data</caption>
  <thead>
    <tr>
      <th scope="col">Month</th>
      <th scope="col">Sales</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">January</th>
      <td>$10,000</td>
    </tr>
  </tbody>
</table>

ARIA Attributes

ARIA (Accessible Rich Internet Applications) attributes enhance accessibility when HTML semantics aren't sufficient.

<!-- ARIA roles -->
<button role="button">Click me</button>
<div role="navigation">Navigation</div>
<div role="complementary">Sidebar</div>

<!-- ARIA labels -->
<button aria-label="Close dialog">✕</button>
<img src="chart.png" alt="Sales chart showing 20% increase" />

<!-- ARIA descriptions -->
<input 
  type="password" 
  aria-describedby="password-help"
/>
<div id="password-help">
  Must be at least 8 characters
</div>

<!-- ARIA states and properties -->
<button aria-pressed="false">Toggle</button>
<div aria-expanded="false">Collapsible content</div>
<div aria-hidden="true">Hidden from screen readers</div>

<!-- Live regions -->
<div aria-live="polite" aria-atomic="true">
  Status updates appear here
</div>

<!-- Custom components -->
<div role="button" tabindex="0" aria-label="Play video">
  ▶
</div>

<!-- Progress indicators -->
<div role="progressbar" 
     aria-valuenow="75" 
     aria-valuemin="0" 
     aria-valuemax="100">
  75% complete
</div>

<!-- Tabs -->
<div role="tablist">
  <button role="tab" aria-selected="true" aria-controls="panel1">
    Tab 1
  </button>
  <button role="tab" aria-selected="false" aria-controls="panel2">
    Tab 2
  </button>
</div>
<div id="panel1" role="tabpanel">Content 1</div>
<div id="panel2" role="tabpanel" hidden>Content 2</div>

Keyboard Navigation

Ensure your website is fully navigable using only a keyboard for users who can't use a mouse.

// React keyboard navigation example
import { useState, useEffect, useRef } from 'react'

function KeyboardAccessibleButton({ onClick, children }) {
  const buttonRef = useRef(null)
  
  const handleKeyDown = (e) => {
    if (e.key === 'Enter' || e.key === ' ') {
      e.preventDefault()
      onClick()
    }
  }
  
  return (
    <div
      ref={buttonRef}
      role="button"
      tabIndex={0}
      onKeyDown={handleKeyDown}
      onClick={onClick}
      aria-label="Action button"
    >
      {children}
    </div>
  )
}

// Focus management
function Modal({ isOpen, onClose }) {
  const modalRef = useRef(null)
  
  useEffect(() => {
    if (isOpen) {
      modalRef.current?.focus()
      // Trap focus within modal
    }
  }, [isOpen])
  
  if (!isOpen) return null
  
  return (
    <div 
      ref={modalRef}
      role="dialog"
      aria-modal="true"
      aria-labelledby="modal-title"
      tabIndex={-1}
    >
      <h2 id="modal-title">Modal Title</h2>
      <button onClick={onClose}>Close</button>
    </div>
  )
}

// Skip to main content link
function SkipLink() {
  return (
    <a
      href="#main-content"
      className="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4"
    >
      Skip to main content
    </a>
  )
}

// Focus visible styles
button:focus-visible {
  outline: 3px solid #4f46e5;
  outline-offset: 2px;
}

Keyboard Best Practices

  • All interactive elements must be keyboard accessible
  • Provide visible focus indicators
  • Implement logical tab order
  • Add skip navigation links

Color and Visual Design

Ensure sufficient color contrast and don't rely solely on color to convey information.

/* WCAG AA contrast ratios */
/* Normal text: 4.5:1 */
/* Large text (18pt+): 3:1 */
/* UI components: 3:1 */

/* Good contrast */
.text-primary {
  color: #1a1a1a; /* On white: 16.5:1 */
}

.text-secondary {
  color: #4a4a4a; /* On white: 7.1:1 */
}

/* Bad contrast */
.text-low-contrast {
  color: #cccccc; /* On white: 1.6:1 - fails */
}

/* Don't rely on color alone */
.error-message {
  color: #dc2626;
  font-weight: bold;
  border-left: 4px solid #dc2626;
  padding-left: 8px;
}

/* Better: Add icon and text */
.error-message {
  color: #dc2626;
  display: flex;
  align-items: center;
  gap: 8px;
}

.error-message::before {
  content: "⚠";
  font-size: 1.2em;
}

/* Focus indicators */
button:focus {
  outline: 3px solid #4f46e5;
  outline-offset: 2px;
}

/* Reduced motion support */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

Accessible Forms

Make forms accessible with proper labels, error handling, and validation feedback.

// React accessible form
function ContactForm() {
  const [errors, setErrors] = useState({})
  
  const handleSubmit = (e) => {
    e.preventDefault()
    // Validate and show errors
  }
  
  return (
    <form onSubmit={handleSubmit} noValidate>
      <div>
        <label htmlFor="name">
          Name <span aria-hidden="true">*</span>
        </label>
        <input
          type="text"
          id="name"
          name="name"
          required
          aria-invalid={errors.name ? 'true' : 'false'}
          aria-describedby={errors.name ? 'name-error' : undefined}
        />
        {errors.name && (
          <span id="name-error" role="alert" className="error">
            {errors.name}
          </span>
        )}
      </div>
      
      <div>
        <label htmlFor="email">Email</label>
        <input
          type="email"
          id="email"
          name="email"
          required
          aria-invalid={errors.email ? 'true' : 'false'}
          aria-describedby={errors.email ? 'email-error' : 'email-help'}
        />
        <small id="email-help">We'll never share your email.</small>
        {errors.email && (
          <span id="email-error" role="alert" className="error">
            {errors.email}
          </span>
        )}
      </div>
      
      <button type="submit">Submit</button>
    </form>
  )
}

// Fieldset for related form controls
<fieldset>
  <legend>Choose your preferences</legend>
  
  <div>
    <input type="checkbox" id="newsletter" name="newsletter" />
    <label for="newsletter">Subscribe to newsletter</label>
  </div>
  
  <div>
    <input type="checkbox" id="updates" name="updates" />
    <label for="updates">Receive product updates</label>
  </div>
</fieldset>

Testing Accessibility

Use tools and techniques to test and validate accessibility throughout development.

# Automated testing tools
# axe DevTools (Chrome extension)
# Lighthouse (Chrome DevTools)
# WAVE (WebAIM)
# axe-core (npm package)

# Install axe-core
npm install --save-dev @axe-core/react

# React testing with axe
import { axe, toHaveNoViolations } from 'jest-axe'
import { render } from '@testing-library/react'

expect.extend(toHaveNoViolations)

it('should have no accessibility violations', async () => {
  const { container } = render(<MyComponent />)
  const results = await axe(container)
  expect(results).toHaveNoViolations()
})

# Manual testing checklist
- Navigate with keyboard only
- Test with screen reader (NVDA, JAWS, VoiceOver)
- Check color contrast ratios
- Verify all images have alt text
- Test form validation and error messages
- Check focus management
- Test with screen magnification
- Verify text resizing works

# Browser testing
# Chrome: Lighthouse audit
# Firefox: Accessibility Inspector
# Safari: VoiceOver testing
# Edge: Accessibility Insights

WCAG Compliance

Web Content Accessibility Guidelines (WCAG) provide standards for accessible web content.

# WCAG 2.1 Principles (POUR)
# Perceivable: Information must be presentable
# Operable: Interface must be operable
# Understandable: Information must be understandable
# Robust: Content must be robust enough for assistive tech

# WCAG Levels
# A: Minimum accessibility
# AA: Standard accessibility (recommended)
# AAA: Enhanced accessibility (not required)

# Key WCAG Success Criteria
# 1.1.1 Non-text Content: Alt text for images
# 1.3.1 Info and Relationships: Semantic HTML
# 1.4.3 Contrast (Minimum): 4.5:1 for normal text
# 2.1.1 Keyboard: All functionality keyboard accessible
# 2.4.1 Bypass Blocks: Skip navigation link
# 2.4.2 Page Titled: Descriptive page titles
# 3.1.1 Language of Page: HTML lang attribute
# 3.3.1 Error Identification: Clear error messages
# 4.1.1 Parsing: Valid HTML
# 4.1.2 Name, Role, Value: ARIA attributes

Accessibility Best Practices

HTML

  • • Use semantic elements
  • • Provide alt text for images
  • • Use proper heading hierarchy
  • • Label form controls

CSS

  • • Ensure color contrast
  • • Support text resizing
  • • Respect reduced motion
  • • Provide focus indicators

JavaScript

  • • Manage focus properly
  • • Handle keyboard events
  • • Use ARIA attributes
  • • Announce dynamic changes

Testing

  • • Test with keyboard
  • • Use screen readers
  • • Run automated audits
  • • Test with real users

Conclusion

Web accessibility is essential for creating inclusive experiences. By following WCAG guidelines, using semantic HTML, implementing proper ARIA attributes, and testing thoroughly, you can ensure your website works for everyone.

Remember that accessibility is not a one-time task but an ongoing process. Make it part of your development workflow from the start.

Ready to Build Accessible Websites?

Explore more web development tutorials and create inclusive experiences!