How to build accessible forms
Labels, errors, grouping and autofill — the details that decide whether a form can be completed at all.
UT Studio7 min read
Forms are where accessibility failures cost the most, because a form that cannot be completed is a transaction that does not happen. Most of the failures come from the same handful of decisions.
A label is not a placeholder
Placeholder-as-label fails four ways at once: it disappears as soon as typing begins, it usually fails contrast, it is not reliably announced, and it leaves nothing to click.
<div>
<label htmlFor="email">Email address</label>
<input
id="email"
name="email"
type="email"
autoComplete="email"
aria-describedby="email-hint"
/>
<p id="email-hint">We send your licence key here.</p>
</div>Errors must be associated, not merely adjacent
Red text under an input is invisible to a screen reader unless it is connected to the field. `aria-describedby` connects it; `aria-invalid` marks the state.
<input
id="email"
aria-invalid={error !== undefined}
aria-describedby={error === undefined ? "email-hint" : "email-error"}
/>
{error !== undefined && (
<p id="email-error" role="alert">{error}</p>
)}Do not move focus to the first error on submit
It sounds helpful and it disorients: the page jumps and the user loses their place. Announce the count in a live region and let them navigate.
Group related fields
Radio groups and checkbox sets need a `fieldset` and a `legend`. Without them, a screen reader announces "Standard, radio button" with no indication of what question it answers.
<fieldset>
<legend>Delivery speed</legend>
<label><input type="radio" name="speed" value="standard" /> Standard</label>
<label><input type="radio" name="speed" value="express" /> Express</label>
</fieldset>Let autofill work
Correct `autocomplete` values are the highest-value five minutes in any form. They help everyone, and they disproportionately help people for whom typing is difficult.
| Field | autocomplete |
|---|---|
| Full name | name |
| Address line 1 | address-line1 |
| Postcode | postal-code |
| Country | country-name |
| New password | new-password |
Required, and how to say so
Use the `required` attribute rather than an asterisk and a legend. Mark the shorter set: if most fields are required, mark the optional ones instead.
The five-minute check
- Complete the form using only the keyboard.
- Click each label and confirm focus moves to its field.
- Submit it empty and read the errors with the screen reader on.
- Zoom to 200% and check nothing is clipped.
- Test autofill with a saved profile.
Templates with substantial form surfaces
Templates mentioned here
Everything above is written against real products. These are the ones this page draws on.
