Accessibility: for everyone, not optional
Accessibility (abbreviated a11y: "a", 11 letters, "y") is building interfaces that anyone can use, including those who navigate with a screen reader, with the keyboard only, or with low vision. It's not an extra: it's software quality (and, often, a legal requirement).
The good news: 90% of accessibility is well-done HTML.
1) Semantic HTML
Use the correct element for each thing. A <button> is already focusable,
clickable with Enter/Space, and announced as a button; a <div onClick> is none
of that.
// ❌ Bad: a div is not keyboard accessible nor announced as a button
<div onClick={close}>Close</div>
// ✅ Good
<button onClick={close}>Close</button>
Same with <nav>, <main>, <header>, <ul>/<li>, headings <h1>…<h6>
in order: they give structure that assistive technologies understand.
2) Labels associated with their fields
Every form control needs an associated label. The robust way is
<label htmlFor={id}> pointing to the field's id. In JSX it's written htmlFor
(not for, which is a reserved word in JS), but in the real DOM it becomes the
for attribute.
<label htmlFor="email">Email</label>
<input id="email" type="email" />
By associating them: the screen reader announces the label when the field is focused, and clicking the label focuses the input (better usability for everyone).
3) ARIA, only when needed
The ARIA attributes (aria-*) add semantic information that HTML by itself
doesn't express. The first rule of ARIA is: don't use ARIA if a native
element already does the job. But it's very useful for nuances:
<input aria-required="true" aria-invalid={hasError} />
<button aria-label="Close dialog">✕</button> {/* button with only an icon */}
<nav aria-label="Main">…</nav>
aria-labelgives an accessible name when there's no visible text (a button with only an icon).aria-required,aria-invalid,aria-expanded… communicate state to assistive technologies.
4) Focus and keyboard navigation
Many people navigate without a mouse. Make sure that:
- Everything interactive is focusable and keyboard-operable (using
<button>,<a href>, inputs… gives it for free). - The tab order follows the visual order.
- The focus is visible (don't remove the
outlinewithout providing an alternative). - In complex components (modals, menus) you manage focus with
tabIndex,onKeyDownand, if needed, moving focus with aref.
5) Unique IDs with useId
Associating label and input by id has a problem in React: if the component is
used multiple times on the page, a fixed id ("email") would be duplicated, and
ids must be unique. The solution is the useId hook, which generates a
unique and stable identifier (it also works well with SSR/hydration):
function Field({ label }) {
const id = React.useId();
return (
<p>
<label htmlFor={id}>{label}</label>
<input id={id} />
</p>
);
}
Important:
useIdis for accessibility ids (linking elements), not for list keys. Reuse the same id (with suffixes if you need several:${id}-error) to relate elements within the same component.
In this runtime's exercises, use
React.useId()(with theReact.prefix) to generate the id.