Building Accessible React Components from Scratch

Learn the principles and patterns for building truly accessible React components that work for all users.

AN

Aisha Nakamura

2 min read·December 12, 2024
Building Accessible React Components from Scratch

Building Accessible React Components from Scratch

Accessibility isn't an afterthought—it's a fundamental aspect of good design. Here's how to build React components that work for everyone.

Why Accessibility Matters

  • 15%+ of the world has some form of disability
  • Legal requirements in many jurisdictions
  • Better UX for everyone: Accessibility improvements benefit all users
  • SEO benefits: Semantic HTML helps search engines

Core Principles

1. Semantic HTML

Use the right element for the job:

// Bad
<div onClick={handleClick}>Click me</div>

// Good
<button onClick={handleClick}>Click me</button>

2. Keyboard Navigation

Everything clickable must be keyboard accessible:

function Button({ onClick, children }) {
  return (
    <button
      onClick={onClick}
      onKeyDown={(e) => {
        if (e.key === 'Enter' || e.key === ' ') {
          onClick(e);
        }
      }}
    >
      {children}
    </button>
  );
}

3. ARIA When Needed

Use ARIA attributes to enhance semantics:

AttributePurpose
aria-labelProvides accessible name
aria-describedbyLinks to description
aria-expandedIndicates toggle state
aria-hiddenHides from assistive tech

Building Common Components

Accessible Modal

function Modal({ isOpen, onClose, title, children }) {
  const modalRef = useRef(null);

  useEffect(() => {
    if (isOpen) {
      modalRef.current?.focus();
      document.body.style.overflow = 'hidden';
    }
    return () => {
      document.body.style.overflow = '';
    };
  }, [isOpen]);

  if (!isOpen) return null;

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

Accessible Dropdown

Key requirements:

  • Arrow key navigation
  • Escape to close
  • Focus management
  • Screen reader announcements

Testing Accessibility

Automated Testing

// Using jest-axe
import { axe, toHaveNoViolations } from 'jest-axe';

expect.extend(toHaveNoViolations);

test('button has no accessibility violations', async () => {
  const { container } = render(<Button>Click me</Button>);
  const results = await axe(container);
  expect(results).toHaveNoViolations();
});

Manual Testing

  1. Keyboard only: Navigate without a mouse
  2. Screen reader: Test with VoiceOver/NVDA
  3. Zoom: Test at 200% zoom
  4. Color contrast: Check with contrast checkers

Resources

  • WCAG 2.1 Guidelines
  • WAI-ARIA Practices
  • Inclusive Components by Heydon Pickering
  • A11y Project

What accessibility challenges have you faced? Share below!

AN

Written by

Aisha Nakamura

Design systems lead at Figma. Sharing insights on UI/UX, accessibility, and building cohesive design languages.

2,877 views
1
Share

Responses (1)

?

10 more characters needed

PY
Patricia YangJanuary 14, 2026

Bookmarked! The modal example is exactly what I've been looking for. Proper focus management is so often overlooked and it makes such a difference for keyboard users.