Accessible Forms Design
Forms are where users complete the most critical tasks on your site — and where accessibility failures are most disabling. Here is how to get it right.
Label Association
Every form input must have a programmatically associated label (WCAG 2.2 SC 1.3.1, 3.3.2). This enables screen readers to announce the field's purpose when focused, and makes the click target larger by allowing users to click the label to focus the field.
Method 1: for/id association (preferred)
<label for="email">Email address</label>
<input
type="email"
id="email"
name="email"
autocomplete="email"
/>
Method 2: wrapping label
<label>
Email address
<input type="email" name="email" autocomplete="email"/>
</label>
Method 3: aria-label (when no visible label)
<!-- Use sparingly — visible labels are always preferred -->
<input
type="search"
aria-label="Search the site"
placeholder="Search..."
/>
Never rely on placeholder text as a label. Placeholders disappear when typing begins, have insufficient contrast in most browsers, and are not reliably announced by all screen readers.
Required Fields
Required fields must be identified both visually and programmatically. If you use an asterisk (*), explain its meaning at the start of the form.
<!-- Explain the asterisk -->
<p>Fields marked with <span aria-hidden="true">*</span> are required.</p>
<label for="name">
Full name
<span aria-hidden="true">*</span>
<span class="sr-only">(required)</span>
</label>
<input
type="text"
id="name"
required
aria-required="true"
/>
Input Types
Use the correct type attribute on inputs. This provides the right virtual keyboard on mobile, enables browser autofill, and communicates the expected format to assistive technology.
| Type | Use For | Mobile Keyboard |
|---|---|---|
type="email" | Email addresses | Shows @ and . keys |
type="tel" | Phone numbers | Numeric pad |
type="number" | Numeric values (quantities) | Numeric pad |
type="url" | Web addresses | Shows .com and / keys |
type="search" | Search inputs | Shows Search button |
type="date" | Calendar date selection | Date picker widget |
type="password" | Password inputs | Masked keyboard |
type="checkbox" | Multiple selection | — |
type="radio" | Single selection from a group | — |
Error Handling & Validation
Form errors are one of the most challenging areas for accessible design. WCAG 2.2 SC 3.3.1 (Error Identification) and 3.3.3 (Error Suggestion) require errors to be clearly identified with text descriptions and suggestions for fixing them.
Accessible error pattern
<div class="field-group">
<label for="email">Email address</label>
<input
type="email"
id="email"
aria-invalid="true"
aria-describedby="email-error"
/>
<p id="email-error" role="alert">
<i class="fas fa-xmark-circle" aria-hidden="true"></i>
Please enter a valid email address (example: [email protected]).
</p>
</div>
Move focus to error summary after failed submission. A summary listing all errors at the top of the form (or the first error) helps screen reader users quickly understand what needs fixing.
Validate on submission, not on every keystroke. Immediate error feedback while typing is disruptive for screen reader users. Validate on blur (when the user leaves a field) or on form submission.
Never rely on color alone to indicate an error. Red border + error icon + error message text = accessible. Red border alone = fail.
Fieldsets & Legends
When a group of related controls share a common purpose (radio buttons, checkboxes), wrap them in a <fieldset> with a <legend> describing the group. Screen readers prepend the legend to each control's label.
<fieldset>
<legend>Preferred contact method</legend>
<label><input type="radio" name="contact" value="email"/> Email</label>
<label><input type="radio" name="contact" value="phone"/> Phone</label>
<label><input type="radio" name="contact" value="text"/> Text message</label>
</fieldset>
When a screen reader focuses on the "Email" radio button above, it announces: "Preferred contact method. Email. Radio button." The legend provides essential context.
Placeholder vs Label
Placeholder
- Disappears when user types
- Often fails 4.5:1 contrast ratio
- Not consistently announced by screen readers
- Cannot serve as a label alone
- Good use: hint text ("e.g., [email protected]")
Label
- Always visible — even after typing
- Increases click target size
- Reliably announced by all screen readers
- Required for all form inputs
- Can be paired with placeholder hint text
Keyboard Navigation
All form interactions must be fully keyboard-accessible (WCAG 2.2 SC 2.1.1). Key behaviors to verify:
| Key | Expected Behavior |
|---|---|
| Tab | Move focus to next focusable element |
| Shift+Tab | Move focus to previous focusable element |
| Space | Toggle checkboxes; activate buttons |
| Enter | Submit form; follow links; activate buttons |
| ↑ ↓ | Move between radio button options in a group |
| Esc | Close dropdowns, date pickers, and custom select widgets |
Ensure focus order follows a logical, meaningful sequence (top-to-bottom, left-to-right). Multi-column form layouts can break logical tab order if not carefully structured.
ARIA for Forms
When native HTML semantics are insufficient, ARIA attributes can enhance form accessibility:
aria-required="true"
Use in addition to the required attribute. Some older assistive technologies only respond to one or the other.
aria-invalid="true"
Set after failed validation. Causes screen readers to announce "invalid" when the field is focused.
aria-describedby
Associate helper text (hints, character counts, format requirements) with an input field programmatically.
aria-errormessage
Newer attribute that explicitly points to an error message element. Use alongside aria-invalid. Has less support than aria-describedby currently.
aria-live="polite"
On a container that will receive dynamic validation messages. Causes the message to be read when the user pauses.
aria-expanded
On custom select/combobox inputs to communicate whether the dropdown is open or closed.
Complete Accessible Form Example
Here is a complete, accessible contact form example incorporating all the principles above:
<form novalidate aria-label="Contact us">
<!-- Error summary (shown after failed submission) -->
<div role="alert" id="form-errors" aria-live="assertive"></div>
<p>Fields marked <span aria-hidden="true">*</span><span class="sr-only">with an asterisk</span> are required.</p>
<div class="field-group">
<label for="fullname">
Full name <span aria-hidden="true">*</span></label>
<input
type="text" id="fullname" name="fullname"
autocomplete="name"
required aria-required="true"
/>
</div>
<div class="field-group">
<label for="email">
Email address <span aria-hidden="true">*</span></label>
<input
type="email" id="email" name="email"
autocomplete="email"
required aria-required="true"
aria-describedby="email-hint"
/>
<p id="email-hint" class="hint-text">Example: [email protected]</p>
</div>
<fieldset>
<legend>Service interest</legend>
<label><input type="checkbox" name="services" value="audit"/> Accessibility Audit</label>
<label><input type="checkbox" name="services" value="remediation"/> Remediation</label>
<label><input type="checkbox" name="services" value="training"/> Training</label>
</fieldset>
<button type="submit">Send Message</button>
</form>
Need a Form Accessibility Audit?
Our team will audit every form on your site against WCAG 2.2 AA and provide detailed, actionable remediation guidance.
Get a Forms Audit