Front-end Engineering Lab
Patterns

Accessibility Patterns

Advanced patterns for building accessible web applications

Accessibility (a11y) ensures your app works for everyone, including users with disabilities. This section covers advanced patterns beyond basic WCAG compliance.

Why Advanced Accessibility?

Basic: Adding alt text, semantic HTML
Advanced: Screen reader testing, focus management, motion control

Impact:

  • 15% of global population has disabilities
  • Legal requirements (ADA, Section 508)
  • Better UX for everyone
  • SEO benefits

Core Principles (WCAG 2.2)

Perceivable

Content must be presentable in ways users can perceive

Operable

Interface must be operable by everyone

Understandable

Content and operation must be understandable

Robust

Content must work with current and future technologies

Coverage

This section includes:

  1. Screen Reader Testing - NVDA, JAWS, VoiceOver strategies
  2. ARIA Live Regions - Dynamic content announcements
  3. Focus Management - SPA focus patterns
  4. Keyboard Shortcuts - Custom accessible shortcuts
  5. Accessible Animations - Motion accessibility
  6. Color Contrast - WCAG AAA compliance
  7. Complex Forms - Multi-step, conditional forms

Quick Start

// Basic accessible component
export function AccessibleButton({ onClick, children }: Props) {
  return (
    <button
      onClick={onClick}
      type="button"
      aria-label="Descriptive label"
    >
      {children}
    </button>
  );
}

// With keyboard support
export function AccessibleDialog({ isOpen, onClose, children }: Props) {
  const dialogRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (isOpen) {
      // Trap focus
      const firstFocusable = dialogRef.current?.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
      (firstFocusable as HTMLElement)?.focus();
    }
  }, [isOpen]);

  return (
    <div
      ref={dialogRef}
      role="dialog"
      aria-modal="true"
      aria-labelledby="dialog-title"
    >
      <h2 id="dialog-title">Dialog Title</h2>
      {children}
      <button onClick={onClose}>Close</button>
    </div>
  );
}

Testing Tools

# Automated testing
npm install -D axe-core @axe-core/react

# Screen readers
- NVDA (Windows) - Free
- JAWS (Windows) - Commercial
- VoiceOver (Mac/iOS) - Built-in
- TalkBack (Android) - Built-in

# Browser extensions
- axe DevTools
- WAVE
- Accessibility Insights

Common Patterns

export function SkipLinks() {
  return (
    <a href="#main-content" className="skip-link">
      Skip to main content
    </a>
  );
}

// CSS
.skip-link {
  position: absolute;
  top: -40px;
  left: 0;
  background: #000;
  color: #fff;
  padding: 8px;
  z-index: 100;
}

.skip-link:focus {
  top: 0;
}

Focus Indicators

/* Clear focus indicators */
*:focus-visible {
  outline: 2px solid #0066cc;
  outline-offset: 2px;
}

/* Context-specific focus */
button:focus-visible {
  outline-color: #0066cc;
}

input:focus-visible {
  outline-color: #00aa00;
}

Semantic HTML

// ❌ BAD: Div soup
<div onClick={handleClick}>Click me</div>

// ✅ GOOD: Semantic button
<button onClick={handleClick}>Click me</button>

// ❌ BAD: No landmarks
<div id="nav">...</div>
<div id="content">...</div>

// ✅ GOOD: Semantic landmarks
<nav>...</nav>
<main>...</main>
<aside>...</aside>

Best Practices

  1. Use semantic HTML first
  2. Test with real screen readers
  3. Keyboard navigation for all features
  4. Color contrast minimum 4.5:1
  5. Focus management in SPAs
  6. ARIA only when HTML isn't enough
  7. Error messages clear and associated
  8. Loading states announced
  9. Form labels always present
  10. Document language specified

Next Steps

Explore the patterns in this section to build truly accessible applications that work for everyone.

On this page