Form accessibility best practices: build accessible forms people can finish
Forms are where websites make money: contact requests, bookings, quotes, and checkouts. They're also where accessibility problems hurt most, because a person who can't finish the form can't become your customer. These best practices cover most of what goes wrong.
1. Give every field a visible, connected label
Placeholder text isn't a label: it disappears when you type, is often low contrast, and isn't reliably announced. Use a <label> connected to the field with for and id.
<label for="email">Email address</label>
<input id="email" name="email" type="email" autocomplete="email">
A connected label also makes the label clickable, which helps anyone with limited fine motor control. WCAG criteria: 1.3.1 Info and Relationships, 3.3.2 Labels or Instructions, 4.1.2 Name, Role, Value.
2. Put hints where they'll be read
Connect format hints to the field with aria-describedby so screen readers announce them with the label.
<label for="phone">Phone number</label>
<span id="phone-hint">Include your area code</span>
<input id="phone" type="tel" autocomplete="tel" aria-describedby="phone-hint">
3. Mark required fields in text, not just color
Use the required attribute and a visible marker explained at the top of the form, such as "Fields marked with an asterisk (*) are required." Don't rely on red labels alone (1.4.1 Use of Color).
4. Group related choices
Radio buttons and checkboxes need a group label, or a screen reader user hears "Yes" without the question.
<fieldset>
<legend>Preferred contact method</legend>
<input type="radio" id="c-email" name="contact" value="email">
<label for="c-email">Email</label>
<input type="radio" id="c-phone" name="contact" value="phone">
<label for="c-phone">Phone</label>
</fieldset>
5. Write error messages that help
- Say what went wrong and how to fix it: "Enter an email address like name@example.com", not "Invalid input" (3.3.1 Error Identification, 3.3.3 Error Suggestion).
- Show the error next to the field and connect it with
aria-describedby; setaria-invalid="true"on the field. - For longer forms, add an error summary at the top with links to each field, and move focus to it.
- Keep what the person already typed.
<label for="email">Email address</label>
<input id="email" type="email" aria-invalid="true" aria-describedby="email-error">
<p id="email-error">Enter an email address like name@example.com</p>
6. Help people fill it in faster
Add autocomplete values like name, email, tel, and street-address (1.3.5 Identify Input Purpose). Don't make people re-enter information they've already given in the same process (3.3.7 Redundant Entry, new in WCAG 2.2).
7. Make it work with a keyboard
- Every field and button should be reachable with Tab, in a logical order, with visible focus.
- Use a real
<button type="submit">, not a styled<div>. - Don't change pages or submit automatically when someone picks an option in a drop-down (3.2.2 On Input).
8. Be careful with CAPTCHAs and time limits
Puzzle CAPTCHAs can lock people out. WCAG 2.2 adds 3.3.8 Accessible Authentication (Minimum), which limits cognitive tests at login. Prefer invisible spam protection. If a form times out, warn people and let them extend the time.
Test your form in five minutes
- Tab through every field with no mouse.
- Submit it empty. Are the errors clear and next to the fields?
- Turn on a screen reader and listen to each label and error.
- Zoom to 200% and check nothing overlaps.
Our own intake form follows these practices. Feel free to view its source.