React: From Components to Production · Testing and Accessibility

Accessibility Fundamentals in React

Semantic HTML, keyboard support, focus management and ARIA.

Accessible React starts with semantic HTML: buttons for actions, links for navigation, headings in order, lists and landmarks (main, nav). Ensure everything works with a keyboard, focus is visible, and focus is managed in dialogs and after route changes. Use ARIA only to fill gaps (e.g., aria-expanded, aria-live for dynamic messages). Prefer well-tested accessible primitives (React Aria, Radix, native <dialog>) for complex widgets.

export function Disclosure({ title, children }: { title: string; children: React.ReactNode }) {
  const [open, setOpen] = useState(false);
  const panelId = useId();
  return (
    <div>
      <button aria-expanded={open} aria-controls={panelId} onClick={() => setOpen((o) => !o)}>
        {title}
      </button>
      <div id={panelId} hidden={!open}>{children}</div>
    </div>
  );
}

// Announce async results to screen readers
<p role="status" aria-live="polite">{saving ? "Saving…" : "All changes saved"}</p>

Common pitfalls

  • <div onClick> instead of <button>.
  • Removing focus outlines without a visible replacement.
  • Modals that don't trap and restore focus.

Key points

  • Semantic HTML first
  • Keyboard and visible focus
  • ARIA to fill gaps; use accessible primitives

Further reading: react.dev: DOM components

Practice questions

Why is <div onClick> an accessibility problem?

Answer

Divs aren't focusable or announced as buttons, and don't respond to Enter/Space; keyboard and screen reader users can't use them. Use <button>.

What should happen to focus when a modal closes?

Answer

Focus should return to the element that opened it, so keyboard users keep their place.

Open in the app to mark lessons complete, take notes and track your progress.