Skip to main content
Accessibility ensures that your applications are usable by everyone, including people with disabilities. This guide covers accessibility standards and implementation in the FE Monorepo.

Accessibility Standards

Both applications follow:
  • WCAG 2.2 - Web Content Accessibility Guidelines
  • ARIA - Accessible Rich Internet Applications
  • React Aria - Adobe’s accessibility-first component library

React Aria Components

The monorepo uses React Aria Components, which provide built-in accessibility features:
import { Button, TextField, Dialog } from 'react-aria-components'

function LoginForm() {
  return (
    <form>
      <TextField>
        <Label>Email</Label>
        <Input type="email" />
        <FieldError />
      </TextField>
      
      <Button type="submit">Sign In</Button>
    </form>
  )
}
React Aria automatically handles:
  • Keyboard navigation
  • Screen reader support
  • Focus management
  • ARIA attributes
  • Touch/pointer interactions

WCAG 2.2 Compliance

Level A (Minimum)

Provide text alternatives for images:
// ✅ Good
<img src="logo.png" alt="Company Logo" />

// ✅ Decorative images
<img src="decoration.png" alt="" />

// ❌ Bad
<img src="logo.png" />
All functionality must be keyboard accessible:
// ✅ Good - uses button
<Button onPress={handleClick}>Click Me</Button>

// ❌ Bad - div not keyboard accessible
<div onClick={handleClick}>Click Me</div>
Specify the page language:
<html lang="en">
  <!-- content -->
</html>
Use semantic HTML and proper ARIA:
// ✅ Good - semantic HTML
<nav>
  <ul>
    <li><a href="/">Home</a></li>
  </ul>
</nav>

// ❌ Bad - no semantic meaning
<div>
  <div onClick={goHome}>Home</div>
</div>
Ensure sufficient color contrast:
  • Normal text: 4.5:1 ratio
  • Large text (18pt+): 3:1 ratio
/* ✅ Good - high contrast */
.text {
  color: #000000; /* black */
  background: #ffffff; /* white */
  /* Contrast ratio: 21:1 */
}

/* ❌ Bad - low contrast */
.text {
  color: #cccccc; /* light gray */
  background: #ffffff; /* white */
  /* Contrast ratio: 1.6:1 */
}
Ensure keyboard focus is visible:
/* ✅ Good - visible focus */
button:focus-visible {
  outline: 2px solid #0066cc;
  outline-offset: 2px;
}

/* ❌ Bad - removing focus */
button:focus {
  outline: none;
}
Keep navigation consistent across pages:
  • Same order
  • Same styling
  • Same behavior

Semantic HTML

Use proper HTML elements for their intended purpose:
function Page() {
  return (
    <>
      <header>
        <nav>
          <ul>
            <li><a href="/">Home</a></li>
            <li><a href="/about">About</a></li>
          </ul>
        </nav>
      </header>
      
      <main>
        <article>
          <h1>Page Title</h1>
          <p>Content goes here</p>
        </article>
        
        <aside>
          <h2>Related Links</h2>
          <ul>
            <li><a href="/related">Related Article</a></li>
          </ul>
        </aside>
      </main>
      
      <footer>
        <p>&copy; 2024 Company Name</p>
      </footer>
    </>
  )
}

ARIA Best Practices

When to Use ARIA

ARIA should be used when HTML semantics are insufficient:
// ✅ Good - native HTML is better
<button>Click Me</button>

// ❌ Bad - unnecessary ARIA
<div role="button" tabIndex={0}>Click Me</div>

// ✅ Good - ARIA needed for complex widget
<div
  role="tabpanel"
  aria-labelledby="tab-1"
  id="panel-1"
>
  Tab content
</div>

Common ARIA Patterns

import { Dialog, Modal } from 'react-aria-components'

function MyModal({ isOpen, onClose }) {
  return (
    <Modal isDismissable isOpen={isOpen}>
      <Dialog>
        {({ close }) => (
          <>
            <Heading slot="title">Modal Title</Heading>
            <p>Modal content</p>
            <Button onPress={close}>Close</Button>
          </>
        )}
      </Dialog>
    </Modal>
  )
}
React Aria automatically:
  • Traps focus within dialog
  • Returns focus on close
  • Adds proper ARIA attributes
  • Handles ESC key

Forms Accessibility

Labels and Inputs

Always associate labels with inputs:
import { TextField, Label, Input, FieldError } from 'react-aria-components'

function SignupForm() {
  return (
    <form>
      <TextField name="email" type="email" isRequired>
        <Label>Email Address</Label>
        <Input />
        <FieldError />
      </TextField>
      
      <TextField name="password" type="password" isRequired>
        <Label>Password</Label>
        <Input />
        <Text slot="description">
          Must be at least 8 characters
        </Text>
        <FieldError />
      </TextField>
    </form>
  )
}

Error Handling

Provide clear, accessible error messages:
function FormField({ error, ...props }) {
  return (
    <TextField {...props} validationState={error ? 'invalid' : 'valid'}>
      <Label>Email</Label>
      <Input />
      {error && (
        <FieldError>
          {error}
        </FieldError>
      )}
    </TextField>
  )
}

Keyboard Navigation

Expected Behaviors

Tab/Shift+Tab

Navigate between focusable elements

Enter/Space

Activate buttons and links

Arrow Keys

Navigate within composite widgets (menus, tabs, lists)

Escape

Close modals, menus, and popovers

Focus Management

import { useRef } from 'react'
import { Button, Dialog, Modal } from 'react-aria-components'

function MyDialog({ isOpen, onClose }) {
  return (
    <Modal isDismissable isOpen={isOpen} onOpenChange={onClose}>
      <Dialog>
        {({ close }) => (
          <>
            <Heading>Confirm Action</Heading>
            <p>Are you sure you want to continue?</p>
            <Button onPress={close}>Cancel</Button>
            <Button onPress={() => { /* action */ }}>Confirm</Button>
          </>
        )}
      </Dialog>
    </Modal>
  )
}
React Aria Components handle focus management automatically.

Screen Reader Support

Announcing Dynamic Content

Use live regions for dynamic updates:
function Toast({ message }) {
  return (
    <div role="status" aria-live="polite">
      {message}
    </div>
  )
}

function Alert({ message }) {
  return (
    <div role="alert" aria-live="assertive">
      {message}
    </div>
  )
}

Visually Hidden Content

Provide context for screen readers:
import { VisuallyHidden } from 'react-aria'

function IconButton() {
  return (
    <button>
      <TrashIcon />
      <VisuallyHidden>Delete item</VisuallyHidden>
    </button>
  )
}

Testing for Accessibility

Automated Testing

Use accessibility testing libraries:
import { render } from '@testing-library/react'
import { axe, toHaveNoViolations } from 'jest-axe'

expect.extend(toHaveNoViolations)

test('Button is accessible', async () => {
  const { container } = render(<Button>Click me</Button>)
  const results = await axe(container)
  expect(results).toHaveNoViolations()
})

Manual Testing

1

Keyboard Navigation

  • Tab through all interactive elements
  • Verify focus indicators are visible
  • Test all keyboard shortcuts
  • Ensure no keyboard traps
2

Screen Reader Testing

Test with:
  • NVDA (Windows)
  • JAWS (Windows)
  • VoiceOver (macOS/iOS)
  • TalkBack (Android)
3

Zoom and Magnification

  • Test at 200% zoom
  • Verify content reflows properly
  • Check for horizontal scrolling
  • Ensure text remains readable
4

Color and Contrast

  • Use browser DevTools contrast checker
  • Test with grayscale mode
  • Verify color is not the only indicator

Browser Extensions

axe DevTools

Comprehensive accessibility testing in Chrome DevTools

WAVE

Visual feedback on accessibility issues

Lighthouse

Built-in Chrome accessibility audits

Accessibility Insights

Microsoft’s accessibility testing tools

Accessibility Checklist

1

Semantic HTML

  • Use proper heading hierarchy (h1 → h6)
  • Use semantic elements (nav, main, article, aside)
  • Use native form controls
  • Provide skip links for navigation
2

Keyboard Support

  • All interactive elements are keyboard accessible
  • Tab order is logical
  • Focus indicators are visible
  • No keyboard traps
3

Visual Design

  • Sufficient color contrast (4.5:1 for normal text)
  • Text is resizable to 200%
  • No information conveyed by color alone
  • Focus indicators are clearly visible
4

Screen Readers

  • All images have alt text
  • Forms have proper labels
  • Dynamic content is announced
  • Page title is descriptive
5

Testing

  • Run automated tests (axe, Lighthouse)
  • Manual keyboard testing
  • Screen reader testing
  • Test at different zoom levels

Resources

WCAG 2.2

Official Web Content Accessibility Guidelines

Learn Accessibility

Google’s comprehensive accessibility course

React Aria

Adobe’s accessible component library

A11y Project

Community-driven accessibility resources

WebAIM

Web accessibility training and resources

Inclusive Components

Patterns for accessible components

Best Practices

  • Include accessibility in design mockups
  • Consider keyboard navigation in UX flows
  • Plan for screen reader announcements
  • Test early and often
Leverage the built-in accessibility features:
  • Automatic ARIA attributes
  • Keyboard navigation
  • Focus management
  • Screen reader support
  • Recruit users with disabilities for testing
  • Get feedback on actual usage patterns
  • Understand real-world barriers
  • Iterate based on user feedback
  • Note keyboard shortcuts in documentation
  • Explain screen reader behavior
  • Provide accessibility statements
  • Keep documentation up to date

Build docs developers (and LLMs) love