Engineering Playbook

Accessibility (a11y)

WCAG standards, ARIA attributes, and inclusive design practices.

Web Accessibility (a11y)

Accessibility is not a "nice-to-have" feature—it's a fundamental aspect of good web development. Making your applications accessible ensures that everyone, regardless of abilities, can use your products effectively.

WCAG Guidelines

The Web Content Accessibility Guidelines (WCAG) provide the international standard for web accessibility. They're organized around four principles:

Perceivable

Information must be presentable in ways users can perceive.

  • Text Alternatives: Provide alt text for images (alt="Description")
  • Captions & Transcripts: For video and audio content
  • Contrast Ratios:
    • WCAG AA: 4.5:1 for normal text, 3:1 for large text
    • WCAG AAA: 7:1 for normal text, 4.5:1 for large text
  • Responsive Design: Content must remain accessible when zoomed to 200%

Operable

Interface components must be operable.

  • Keyboard Navigation: All functionality available via keyboard
  • No Keyboard Traps: Users can navigate away from all components
  • Focus Management: Clear visible focus indicators
  • Time Limits: Give users control over time-sensitive content

Understandable

Information and UI operation must be understandable.

  • Readable Text: Use simple language, define abbreviations
  • Predictable Navigation: Consistent navigation patterns
  • Input Assistance: Help users avoid and correct mistakes
  • Error Identification: Clear error messages and suggestions

Robust

Content must be robust enough for various assistive technologies.

  • Valid HTML: Proper semantic markup
  • ARIA Roles: Use ARIA attributes correctly
  • Compatibility: Works with current and future assistive tech

Semantic HTML

The foundation of accessibility is proper HTML structure.

<!-- Bad: Div soup -->
<div class="header">...</div>
<div class="article">...</div>
<div class="button">Click me</div>

<!-- Good: Semantic HTML -->
<header>...</header>
<main>
  <article>...</article>
  <button>Click me</button>
</main>

Essential Semantic Elements

  • <header>, <nav>, <main>, <footer>
  • <section>, <article>, <aside>
  • <h1>-<h6> for proper heading hierarchy
  • <button>, <input>, <select>, <textarea>

ARIA Attributes

Accessible Rich Internet Applications (ARIA) enhance semantic HTML when needed.

ARIA Roles

<!-- Landmark roles -->
<div role="banner">...</div>  <!-- Header -->
<div role="navigation">...</div>  <!-- Nav -->
<div role="main">...</div>  <!-- Main -->
<div role="contentinfo">...</div>  <!-- Footer -->

<!-- Widget roles -->
<div role="tablist">...</div>
<div role="tabpanel">...</div>
<div role="dialog">...</div>

ARIA Properties & States

<!-- Descriptive properties -->
<button aria-label="Close dialog">×</button>
<input aria-describedby="help-text" />

<!-- State indicators -->
<button aria-expanded="false">Menu</button>
<div aria-hidden="true">Hidden content</div>
<input aria-invalid="true" aria-describedby="error-message" />

First Rule of ARIA

Don't use ARIA if native HTML elements suffice. Use semantic HTML first, then enhance with ARIA only when necessary.


Keyboard Navigation

Ensure your application is fully keyboard accessible.

Focus Management

/* Visible focus indicator */
button:focus, input:focus, [tabindex]:focus {
  outline: 2px solid #0066cc;
  outline-offset: 2px;
}

/* Skip to main content link */
.skip-link {
  position: absolute;
  top: -40px;
  left: 6px;
  background: #0066cc;
  color: white;
  padding: 8px;
  text-decoration: none;
  z-index: 1000;
}

.skip-link:focus {
  top: 6px;
}

Tab Order

// Custom component with proper tab management
function CustomDropdown() {
  const [isOpen, setIsOpen] = useState(false);
  const dropdownRef = useRef(null);
  
  useEffect(() => {
    if (isOpen) {
      dropdownRef.current?.focus();
    }
  }, [isOpen]);
  
  return (
    <div>
      <button 
        aria-expanded={isOpen}
        aria-haspopup="menu"
        onClick={() => setIsOpen(!isOpen)}
      >
        Menu
      </button>
      {isOpen && (
        <ul role="menu" ref={dropdownRef}>
          <li role="menuitem"><a href="/item1">Item 1</a></li>
          <li role="menuitem"><a href="/item2">Item 2</a></li>
        </ul>
      )}
    </div>
  );
}

Screen Reader Considerations

Testing with Screen Readers

  • VoiceOver (macOS/iOS): Cmd + F5
  • NVDA (Windows): Free, popular screen reader
  • JAWS (Windows): Commercial, widely used
  • TalkBack (Android): Built-in screen reader

Screen Reader Best Practices

  • Use heading hierarchy (<h1><h2><h3>)
  • Provide context for links: "Read more about accessibility" vs "Read more"
  • Announce dynamic content changes: aria-live="polite" or aria-live="assertive"
  • Use landmarks for navigation: <nav>, <main>, <aside>
// Live region for dynamic content
<div aria-live="polite" aria-atomic="true">
  {notification && <p>{notification}</p>}
</div>

Testing Tools

Automated Testing

# ESLint with a11y rules
npm install eslint-plugin-jsx-a11y

# React testing with a11y assertions
npm install @testing-library/jest-dom
npm install jest-axe
// Unit testing with jest-axe
import { axe, toHaveNoViolations } from 'jest-axe';
import { render } from '@testing-library/react';
import MyComponent from './MyComponent';

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

Browser Extensions

  • axe DevTools Chrome extension
  • WAVE Web Accessibility Evaluation Tool
  • Color Contrast Analyzer
  • HeadingsMap for heading structure validation

Common Accessibility Issues & Solutions

1. Missing Alt Text

<!-- Bad -->
<img src="chart.png">

<!-- Good -->
<img src="chart.png" alt="Q3 sales increased by 25% compared to Q2">
<img src="decorative-line.png" alt="" role="presentation"> <!-- Decorative -->

2. Poor Color Contrast

/* Bad: Low contrast */
.text { color: #999; background: #fff; } /* 3:1 ratio */

/* Good: WCAG AA compliant */
.text { color: #555; background: #fff; } /* 7:1 ratio */

3. Forms Without Labels

<!-- Bad -->
<input type="email" placeholder="Email">

<!-- Good -->
<label for="email">Email</label>
<input type="email" id="email" required aria-describedby="email-help">
<div id="email-help">We'll never share your email</div>
<!-- Bad -->
<a href="/article">Read more</a>

<!-- Good -->
<a href="/article">Read more about accessibility best practices</a>
<a href="/article" aria-label="Read more about accessibility best practices">Read more</a>

Accessibility Checklist

Design Phase

  • Color contrast meets WCAG AA standards
  • Text remains readable at 200% zoom
  • Focus indicators are clearly visible
  • Content structure is logical and predictable

Development Phase

  • Semantic HTML is used appropriately
  • All images have descriptive alt text
  • Form inputs have proper labels
  • Keyboard navigation works for all interactive elements
  • ARIA attributes are used correctly
  • Dynamic content changes are announced

Testing Phase

  • Test with keyboard only
  • Test with screen reader
  • Run automated accessibility tests
  • Test with mobile accessibility features
  • Validate HTML and ARIA implementation

Accessibility is a Team Sport

Accessibility isn't just a frontend responsibility. Designers need to consider color contrast and layout, PMs need to include it in requirements, and QA needs to test with assistive technologies. Everyone plays a role in creating inclusive experiences.