React Accessibility (a11y)

Build inclusive, WCAG-compliant React applications using semantic HTML, ARIA attributes, and keyboard focus management.

TL;DR

  1. Build inclusive web applications using semantic HTML elements.
  2. Enhance assistive technology experiences with accessible ARIA attributes.
  3. Manage interactive keyboard focus transitions smoothly using persistent component DOM refs.

Semantic HTML and ARIA

    Native Button Semantics

    Use native button for keyboard focus and enter key.

    // Accessible by default
    <ButtonBase onClick={handleAction}>
      Submit Form
    </ButtonBase>
    Accessible Icon Button

    Supply text description for icon-only buttons.

    <ButtonBase aria-label="Close Navigation">
      <CloseIcon />
    </ButtonBase>
    Dynamic ARIA States

    Reflect expanded or selected state dynamically.

    <ButtonBase
      aria-expanded={isOpen}
      aria-controls="dropdown-menu"
      onClick={toggle}
    >
      Menu
    </ButtonBase>

Focus Management

    Focus on Mount with Ref

    Shift focus to modal or input upon appearance.

    const firstInputRef = useRef<HTMLInputElement>(null);
    useEffect(() => {
      firstInputRef.current?.focus();
    }, []);
    Skip Navigation Link

    Provide fast skip link for keyboard-only users.

    <Link
      href="#main-content"
      className="sr-only focus:not-sr-only"
    >
      Skip to Main Content
    </Link>
    Screen Reader Only Style

    Hide text visually while keeping it accessible.

    // Visual text hidden but read by screen readers
    <VisuallyHidden>Current step: 2 of 5</VisuallyHidden>

Keyboard and Live Regions

    Keyboard Handler Pattern

    Listen for Enter and Space on custom controls.

    const onKey = (e: React.KeyboardEvent) => {
      if (e.key === 'Enter' || e.key === ' ') {
        e.preventDefault();
        onToggle();
      }
    };
    Polite Live Region

    Announce status updates without interrupting speech.

    <Alert role="status" aria-live="polite">
      {savedStatus}
    </Alert>
    Form Error Association

    Link input error message using aria-describedby.

    <InputBase
      id="user-email"
      aria-invalid={hasError}
      aria-describedby="email-error"
    />
    {hasError && <Text id="email-error">Invalid</Text>}

Form and Field Accessibility

    Accessible Fieldset Group

    Group related radio or checkbox inputs with legend.

    <FieldsetGroup>
      <LegendTitle>Preferences</LegendTitle>
      <Checkbox label="Email alerts" />
    </FieldsetGroup>
    Input Label Association

    Associate form labels explicitly using htmlFor ID.

    <LabelText htmlFor="user-name">Name</LabelText>
    <InputBase id="user-name" />
    Error Feedback Announcement

    Announce form errors immediately with role alert.

    <Text role="alert" aria-live="assertive">
      {errorMessage}
    </Text>

Tips

  1. Always prefer native HTML elements like <button> and <dialog> over custom div tags with ARIA role attributes.
  2. Ensure all interactive custom components support Enter, Space, and Escape keyboard navigation with visible :focus indicators.

Warnings

  1. Never attach onClick listeners to non-interactive elements like div or span without providing keyboard event listeners.
  2. Avoid using positive tabIndex values greater than zero because they distort natural document tab order unexpectedly.

In Practice

FAQ