Accessible Forms: A WCAG Checklist for Developers
Build forms that work for keyboard and screen-reader users, with a practical WCAG 2.2 checklist.
An inaccessible form quietly turns people away — keyboard users who can’t reach the submit button, screen-reader users who hit an unlabelled field, anyone who can’t tell which input failed. This developer checklist walks through the markup that makes forms work for everyone and meets WCAG 2.2 AA.
1. Label every control
This is the single biggest win. Each input needs a programmatically associated <label> — either wrapping the input or linked with for/id.
<!-- Explicit association --> <label for="email">Email address</label> <input id="email" name="email" type="email" required /> <!-- Or wrap the input --> <label> Email address <input name="email" type="email" required /> </label>
2. Make it keyboard-operable
- Every control must be reachable and usable with
TabandEnter/Space. - Use real
<button>and<input>elements, not clickable<div>s. - Keep a visible focus indicator — never
outline: nonewithout a replacement. - Ensure the DOM order matches the visual order so focus moves logically.
3. Group related fields
Radio buttons, checkboxes, and multi-part inputs (like an address) belong in a <fieldset> with a <legend> so the group has an accessible name.
<fieldset> <legend>How should we contact you?</legend> <label><input type="radio" name="contact" value="email" /> Email</label> <label><input type="radio" name="contact" value="phone" /> Phone</label> </fieldset>
4. Announce errors
When validation fails, the user needs to know which field and why. Mark the field invalid, point it at its message, and put the message somewhere screen readers will announce.
<label for="email">Email</label> <input id="email" name="email" type="email" aria-invalid="true" aria-describedby="email-error" /> <p id="email-error" role="alert"> Enter an email so we can reply. </p>
5. Mark required fields honestly
Use the required attribute (it maps to aria-required) and indicate it visually in the label text, not with a bare asterisk that screen readers may skip. If most fields are required, mark the optional ones instead.
The WCAG 2.2 quick checklist
- Every input has a real, associated label.
- The whole form works with a keyboard, with a visible focus ring.
- Related controls are grouped with fieldset + legend.
- Errors are announced, tied to their field, and not colour-only.
- Required state is conveyed in text, not just visually.
- Inputs use correct types and autocomplete tokens for autofill.
Pair this with the form validation guide for accessible error handling, then generate labelled, standards-friendly markup with the HTML form code generator.
Frequently asked questions
What makes a form accessible?
Every control has a real, associated label; the form is fully operable by keyboard; focus order is logical; errors are announced and tied to the field that caused them; and nothing relies on colour alone. Meeting WCAG 2.2 at level AA is the common target.
Is a placeholder a substitute for a label?
No. Placeholder text disappears as soon as the user types, fails colour-contrast guidance, and isn’t reliably announced by screen readers. Always use a real <label> element; use the placeholder only for an example value, if at all.
How do I make form errors accessible?
Set aria-invalid="true" on the failing field, link it to its message with aria-describedby, and put the message in a live region so screen readers announce it. Never signal an error with colour alone — include text and, ideally, an icon.
Do accessible forms help SEO?
Indirectly, yes. Proper labels, semantic markup, and clear structure make a form easier for assistive tech and for crawlers to understand, and the better completion rates that come with accessible forms are a positive signal.
Related resources
Accessible markup, handled backend
You own the accessible HTML; SaveForm handles collection, email, and storage. Generate clean, labelled markup to start from.