awesome-everything RU
↑ Back to the climb

Frontend Architecture

Accessible forms: code reading

Crux Read real JSX snippets — label association, an aria-live error region, focus management, and a controlled input — and pick the highest-leverage accessibility fix.
Your altitude — climbing toward senior
ZeroJuniorMiddleSenior
You are at senior altitude — in orbit
◷ 14 min

Form accessibility is decided in the markup and the submit handler. Read each snippet the way you would in a PR review, then choose the fix a senior engineer makes first.

Goal

Practise the review loop: spot the broken label association, the live region that announces nothing, the missing focus move, and the controlled input that drops the field name — then reach for the correct fix.

Snippet 1 — the label that associates with nothing

function EmailField() {
  return (
    <div className="field">
      <label>Email</label>
      <input type="email" name="email" placeholder="you@example.com" />
    </div>
  );
}
Quiz

A screen reader announces this field as 'edit text' with no name. What is the bug and the fix?

Snippet 2 — the error region that announces nothing

function Form() {
  const [error, setError] = useState("");
  return (
    <form onSubmit={validate}>
      <input id="zip" name="zip" aria-describedby="zip-err" />
      {error && <div id="zip-err" role="alert">{error}</div>}
      <button>Submit</button>
    </form>
  );
}
Quiz

Sighted users see the error; many screen-reader users hear nothing. Why, and what is the minimal fix?

Snippet 3 — the submit handler that forgets focus

function handleSubmit(e) {
  e.preventDefault();
  const invalid = fields.filter((f) => !f.valid);
  invalid.forEach((f) => {
    f.el.setAttribute("aria-invalid", "true");
    setMessage(f.id, f.error); // writes into a persistent live region
  });
  // ...nothing else
}
Quiz

aria-invalid is set and the messages are announced, yet keyboard users say the form 'does nothing' on a failed submit. What is missing?

Snippet 4 — the controlled input that drops its name

function NameField({ value, onChange }) {
  return (
    <>
      <span className="lbl">Full name</span>
      <input
        value={value}
        onChange={(e) => onChange(e.target.value)}
      />
    </>
  );
}
Quiz

This is a correctly controlled React input, yet it fails accessibility. What is the real problem and fix?

Recap

Every form accessibility bug is visible in the markup or the submit handler: a label must be associated by htmlFor/id or wrapping (a placeholder or sibling span is not a name); a live region must be persistent so its text change is observed; a failed submit must imperatively move focus to the first invalid field, not just set aria-invalid; and controlled-vs-uncontrolled is orthogonal to whether the input has an accessible name. Read the markup, find the broken association or the un-moved focus, then fix that before anything else.

Continue the climb ↑Accessible forms: ship a keyboard-complete form
shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.