Accessible Forms
Labels, required fields, grouping, and errors people can recover from
A form is where accessibility problems cost the most. A confusing paragraph is an irritation; a form somebody cannot complete is a task they cannot do at all. It is also where the fixes are the most mechanical, because most form failures come down to a handful of things being missing rather than anything being hard.
This guide covers labels, required fields, grouping and errors. For the autocomplete attribute that lets a browser fill fields in, see Identifying Input Purpose.
Every field needs a real label
<input type="text" placeholder="Email address">
<span>Phone</span> <input type="tel">
- The first field has no label at all.
- A placeholder is not a label: it disappears the moment somebody types, screen readers treat it inconsistently, and its low-contrast grey usually fails the contrast requirement on its own.
- The second has visible text, but nothing connects that text to the input, so a screen reader announces "edit text, blank".
<label for="email">Email address</label>
<input type="text" id="email" autocomplete="email">
<label for="phone">Phone</label>
<input type="tel" id="phone" autocomplete="tel">
The for attribute matches the input's id. That connection does two things: a screen reader announces the label when the field is focused, and clicking the label puts the cursor in the field, which matters for anyone with a motor disability aiming at a small checkbox.
Where a visible label genuinely cannot be used, aria-label on the input is the fallback. Keep it identical to any visible text nearby, because a mismatch between what somebody sees and what their software says is worse than either alone.
Say which fields are required, in words
<label for="name">Name <span style="color:red">*</span></label>
An asterisk in red carries the meaning in two ways some people cannot use: color alone fails for anyone who cannot distinguish it, and a bare asterisk means nothing unless a legend explains it. Screen readers often skip it entirely.
<label for="name">Name (required)</label>
<input type="text" id="name" required>
The word is in the label, and required tells assistive technology the same thing. Keep the asterisk too if the design calls for it, with a legend at the top of the form explaining what it means. The point is that the asterisk is not the only signal.
Group fields that ask one question
A set of radio buttons or checkboxes is one question with several answers, and each individual option label is meaningless without the question. "Morning" tells nobody anything; "Preferred contact time: morning" does.
<fieldset>
<legend>Preferred contact time</legend>
<label><input type="radio" name="time" value="am"> Morning</label>
<label><input type="radio" name="time" value="pm"> Afternoon</label>
</fieldset>
The same applies to a group of fields that together form one thing, such as a name split into first and last, or an address split across several boxes.
Put instructions before the field
Anything somebody needs in order to fill a field in correctly has to come before they reach it. A note under a date box saying "use MM/DD/YYYY" arrives after they have typed, and somebody navigating by keyboard may never encounter it at all, because tabbing moves between fields and skips the text between them.
<label for="dob">Date of birth</label>
<span id="dob-hint">Use MM/DD/YYYY, for example 03/14/1958.</span>
<input type="text" id="dob" aria-describedby="dob-hint" autocomplete="bday">
aria-describedby connects the instruction to the field so it is announced along with the label.
Errors people can act on
Three separate things go wrong with form errors, and all three are common.
- The error is only a color. A red border tells some people nothing. Every error needs text.
- The error does not say what is wrong. "Invalid input" is not actionable. "Enter a date in MM/DD/YYYY format" is.
- The error is not connected to the field. A summary at the top helps sighted users scanning, but somebody moving field by field needs the message attached to the field itself.
<label for="zip">ZIP code (required)</label>
<input type="text" id="zip" aria-describedby="zip-error" aria-invalid="true"
autocomplete="postal-code">
<span id="zip-error">Enter a 5-digit ZIP code, for example 17325.</span>
Where a form fails on submission, move focus to a summary of what went wrong at the top of the form, list each problem as a link to the field it belongs to, and say how many there are. Somebody who cannot see the page has no other way to discover that anything failed.
Do not make a form a memory test. Session timeouts, one-time codes that expire quickly, and multi-step forms that discard entries on a validation failure all fail people with cognitive disabilities first, and everybody eventually. If a form must time out, warn before it does and let the person extend it. If it has several steps, do not throw away what they already typed.
Why it matters
Forms are the point where a library website stops being something to read and becomes something to use: registering for a card, reserving a room, requesting a purchase, asking a question. A form that cannot be completed is a service that cannot be reached, and unlike a page somebody can give up on, there is usually no alternative route to the same outcome.
The failures are also unusually cheap to fix. Most of what is above is a for attribute, a word in a label, or a sentence of error text.