Forms, labels and accessible validation
Programmatic labels and grouping, required and error states, validation that does not hijack focus, help text, and accessible authentication under WCAG 2.2.
Labels that are really labels
<form novalidate>
<!-- an explicit label, linked by id -->
<div class="field">
<label for="email">Email address</label>
<input id="email" name="email" type="email" autocomplete="email"
aria-describedby="email-help" required />
<p id="email-help">We only use this to send order updates.</p>
</div>
<!-- radio groups need a fieldset and a legend, not a label -->
<fieldset>
<legend>Delivery speed</legend>
<label><input type="radio" name="speed" value="standard" /> Standard, 3-5 days</label>
<label><input type="radio" name="speed" value="express" /> Express, next day</label>
</fieldset>
<!-- a checkbox: the label must wrap or be linked, and the target must be big enough -->
<label>
<input type="checkbox" name="terms" required />
I accept the terms of service
</label>
<button type="submit">Place order</button>
</form>- A
placeholderis not a label. It disappears on input, it is often low contrast, and many assistive technologies treat it as a hint rather than a name. - Group related controls in a
fieldsetwith alegend. That is what makes a radio group announced as one question with options. - Set
autocompletecorrectly. It is an accessibility feature - it removes memory and typing work - as well as a convenience. - Associate hint text with
aria-describedbyso it is announced with the field rather than floating unconnected.
Errors and validation
<div class="field field-error">
<label for="card">Card number</label>
<input id="card" name="card" inputmode="numeric" autocomplete="cc-number"
aria-invalid="true" aria-describedby="card-error" />
<p id="card-error" role="alert">
<svg aria-hidden="true" focusable="false">...</svg>
Enter the 16 digits without spaces.
</p>
</div>
<!-- a summary at the top of the form, linked to each field -->
<div role="alert" tabindex="-1" id="error-summary">
<h2>There are 2 problems with this form</h2>
<ul>
<li><a href="#card">Card number: enter the 16 digits without spaces</a></li>
<li><a href="#postcode">Postcode: this does not look like a valid postcode</a></li>
</ul>
</div>- Validate after the field loses focus or on submit, not on every keystroke. Validating mid-typing interrupts the user before they have finished.
- Put the message next to the field, describe how to fix it, and connect it with
aria-describedby. - Set
aria-invalid="true"on the field, and remove it when the value becomes valid. - On submit failure, move focus to an error summary at the top. Do not move focus into the first invalid field - the user then does not know how many problems remain.
- Never rely on a red border alone. Colour is not a message.
- Keep the user's input. Clearing a form on validation failure is a hostile act.
// announce the error without stealing focus mid-typing
function showError(input, message) {
input.setAttribute("aria-invalid", "true");
const el = document.getElementById(input.getAttribute("aria-describedby"));
el.textContent = message;
el.setAttribute("role", "alert"); // announced politely and immediately
}
function clearError(input) {
input.removeAttribute("aria-invalid");
const el = document.getElementById(input.getAttribute("aria-describedby"));
if (el) el.textContent = "";
}Accessible authentication
| Requirement | What it means in practice | Common failure |
|---|---|---|
| Do not block paste | Password managers and paste must work | onpaste="return false" on a password field |
| Allow a cognitive test alternative | Offer another way if a puzzle is required | A CAPTCHA with no audio or alternative |
| No memorised secret required | Or provide a way to complete without it | A code sent to an email the user cannot access |
| Do not ask for the same data twice | Redundant entry is a barrier | Re-entering an address that was just supplied |
| Keep the user's data between steps | A timeout must not destroy the work | Multi-step form that clears on session expiry |
| Allow extra time | Or warn before a timeout and let it be extended | A silent 15-minute session timeout |
- Blocking paste into password or one-time-code fields breaks password managers, which are the single best security control most users have. It makes the site less secure and less accessible at the same time.
- If a puzzle is unavoidable, provide an alternative that does not rely on a cognitive function - for example an email link, or a code generated by the account.
- A password field should have a show/hide control so the user can check what they typed.
- Never cap the length of a password field below the length you accept, and never transform what was entered.
⚠️
The four most common form failures are all avoidable in one line each: a placeholder used as a label, an error shown only by colour, paste blocked on a password field, and a timeout that discards the user's work. Fix those before adding any ARIA.
FAQ
Should every field be validated live?
Only in a positive direction. Confirm a value is acceptable as the user types, but do not raise an error until they leave the field or submit. Interrupting mid-entry is a barrier, not a help.
Do I need a summary if there is one error?
It is usually still worth it, because a summary is the first thing announced after a failed submit. For a single-field form a message beside the field is enough.
Related
Focus management, ARIA and testing Cognitive accessibility: plain language and error recovery
Last refreshed 2026-09-18.