Forms Everyone Can Fill: Labels, Errors, and the Autocomplete Attribute You Keep Forgetting

Most form accessibility bugs aren't exotic — they're a label that isn't wired to its input and an error message nobody's screen reader ever announces. Both are cheap to fix.

requiredSend<label for>aria-describedbyautocomplete="email"
Steve
Accessibility (A11Y) Specialist
Feb 10, 2026
6 min read

My mother-in-law calls me when the internet defeats her, which means I've watched a lot of forms fail in the wild. The most recent one was a rebate form she'd been fighting for twenty minutes. She works at 200% browser zoom, so the top of the page — where the form had helpfully painted "Please correct the errors below" in red — was never on her screen. The fields themselves used placeholder text instead of labels, so every field she'd already typed into had turned into an anonymous box. She had one error somewhere in fourteen fields and no way to find out which.

Nothing about that form was exotic. It's the same handful of gaps I see in code review every month, and every one of them has a fix that costs a few minutes. Forms are where accessibility stops being philosophical: this is where people pay bills, book appointments, and apply for jobs. Let's walk through the three fixes that carry most of the weight.

A label is an API, not a caption

The word "label" makes it sound decorative — text that sits near an input. But a programmatically associated label is a piece of machinery. When the <label>'s htmlFor matches the input's id (or the label wraps the input), three things start working:

  • Screen readers announce the label when the input gets focus: "Email, edit text." Without the association, the user hears "edit text" and has to go hunting for context.
  • Clicking the label focuses the input — a free hit-target upgrade that helps anyone with a tremor, a touchscreen, or a hurry.
  • Voice control users can say "click Email" and land in the right field, because the field actually has that name.

A <span> sitting above the input provides none of this, and a placeholder provides less than none:

// Before: a caption only sighted users get, that vanishes on first keystroke
<span>Email</span>
<input type="email" placeholder="Email" />

// After: associated, clickable, announced, autofillable
<label htmlFor="email">Email</label>
<input id="email" type="email" autoComplete="email" />

Placeholders deserve a special word because they feel like labels. They aren't. They disappear the moment someone types, which is exactly when people with memory or attention challenges need the prompt most — and honestly, when I need it too, mid-interruption. Their default contrast usually fails WCAG. Use placeholders for format hints ("MM/DD/YYYY") if you like, but never as the field's only name.

In review, the check takes seconds: does every input have a <label> with a matching for/htmlFor? Generated form code fails this constantly — models love the placeholder-as-label pattern because the visual result looks clean, so it's worth being extra alert in AI-assisted diffs.

Errors that get announced, not just painted

Here's the uncomfortable question I ask about every form PR: when validation fails, how does a screen reader user find out?

The common answer is "they don't." The error text renders in red near the field, the DOM updates silently, and focus sits wherever it was. Visually the page is screaming; aurally, nothing happened.

Two attributes and one decision fix most of this:

<label htmlFor="card">Card number</label>
<input
  id="card"
  inputMode="numeric"
  autoComplete="cc-number"
  aria-invalid={Boolean(error)}
  aria-describedby={error ? "card-error" : undefined}
/>
{error && (
  <p id="card-error" role="alert">
    Check the card number — it should be 16 digits.
  </p>
)}

aria-invalid tells assistive tech the field is in an error state. aria-describedby wires the error text to the input, so refocusing the field reads the message along with the label — the user gets the problem and the place in one breath. role="alert" makes the message announce itself the moment it appears, without stealing focus.

The decision: on submit, when there are errors, move focus somewhere useful. Either to the first invalid field, or to an error summary at the top that links to each problem field. Pick one and be consistent. A page that fails silently while focus rests on a Submit button that seems to do nothing is one of the most disorienting experiences on the web — with or without a screen reader.

And write the message like a human. "Invalid input" tells nobody anything. Say what's wrong and how to fix it, next to where it went wrong. (One more nudge: don't let red be the only signal — pair it with an icon or bold text, so the state survives color blindness and grayscale screens.)

Autocomplete is assistive technology

The autocomplete attribute gets treated as a browser convenience, but I'd argue it's one of the most underrated accessibility features in HTML. WCAG agrees — success criterion 1.3.5 ("Identify Input Purpose") asks exactly this: tell the browser, in machine-readable terms, what the field is for.

<input id="name"  autocomplete="name" />
<input id="email" autocomplete="email" />
<input id="addr"  autocomplete="street-address" />
<input id="tel"   autocomplete="tel" />

Who does this help? People with motor impairments, for whom every keystroke has a real cost — autofill turns a two-minute address form into one confirmation. People with dyslexia or memory difficulties, who no longer have to transcribe their own email address without typos. And frankly everyone on a phone keyboard at a bus stop. The browser is standing by, ready to do this work; the attribute is you giving it permission.

The spec's token list is worth ten minutes of your time — given-name, postal-code, cc-number, bday, one-time-code (which enables SMS-code auto-fill on mobile), and dozens more. And please resist the reflexive autocomplete="off" on ordinary fields. Teams add it out of vague security instinct, and its main real-world effect is punishing the people who most rely on autofill. Save it for the rare field where a stored value is genuinely wrong, like a one-time challenge answer.

Before you approve that form PR

My whole checklist for a form change fits on a sticky note:

  • Every input has a real <label>, programmatically associated. Placeholders are hints, never names.
  • Errors are wired up with aria-describedby and aria-invalid, and they say something actionable.
  • On a failed submit, focus goes to the first error or an error summary — never nowhere.
  • Personal-data fields carry the right autocomplete token, and no one has sprinkled autocomplete="off" without a reason they can say out loud.
  • Bonus points: group related radios and checkboxes in a <fieldset> with a <legend>, and mark required fields in text, not just with an asterisk of color.

None of this is advanced. That's sort of my point. The gap between a form that quietly excludes people and one that works for everyone is usually a handful of attributes — the kind of thing you can catch in review with your eyes half open, once you know to look. My mother-in-law finished her rebate form, by the way. I filled it in over a screen share. I'd rather the form had just done its job.

formslabelserrorsautocomplete