Forms
Applies to
a11y.form-labels, a11y.form-errors.
Covers a11y.form-labels, a11y.form-errors.
Labels
A placeholder is not a label. It disappears the moment someone starts typing, which is exactly when they need to check what the field was. It fails contrast in most designs, it is not reliably announced, and it leaves anyone returning to a half-filled form guessing.
aria-label is not a label either, in the case that matters. It names the field for a screen
reader and gives nothing at all to the sighted user who has forgotten what they were typing, or to
anyone using speech control, who has to say the visible text.
<!-- No -->
<input type="text" name="lastname" placeholder="Your name *" aria-label="Your name">
<!-- Yes -->
<label for="lastname">Your name <span aria-hidden="true">*</span></label>
<input type="text" id="lastname" name="lastname" required autocomplete="family-name">
If the design has no room for a visible label, that is a design problem to raise, not a markup problem to work around. A floating label that moves out of the field on focus satisfies both.
Add autocomplete tokens on anything personal - given-name, family-name, email, tel,
street-address, postal-code, country-name. It is one attribute, it is a WCAG 2.1 AA
requirement, and it saves every visitor typing their own address again.
Required fields
Mark them in the label text, not with a colour or an asterisk alone. Add the required attribute
so it is exposed programmatically. If an asterisk carries the meaning, explain it once above the
form and hide the asterisk itself from assistive technology.
Errors
Four things, all of them:
- Text that names the problem and the fix. "Enter an email address like name@example.com", not "Invalid input" and not a red border on its own.
- Next to the field, associated with
aria-describedby, and referenced byaria-invalid="true"on the control. - Announced. Put the summary in a live region, or move focus to it, so it is not silent.
- Focus moves to the first invalid field on submit. Do not leave someone at the bottom of a long form with an error they cannot find.
Never rely on colour alone for either the error or the success state. Roughly one man in twelve cannot reliably tell your red border from your grey one.
After submit
Success needs an announcement too. A form that clears itself and says nothing reads as a form that did nothing. Move focus to the confirmation message.
And check the thing that costs the most: confirm the message actually sends. A contact form that reports success and silently drops the enquiry is the single most expensive front-end bug there is, because nobody finds out for weeks.