Skip to main content
Technique

Forms Accessibility Patterns

Accessible form design patterns — labels, error handling, grouping, autocomplete, and validation — with code examples for common form components.

The Foundation: Labels

Every form control must have a programmatically associated label. This is required by WCAG 4.1.2 (Name, Role, Value) and 1.3.1 (Info and Relationships). When a user focuses an input with a screen reader, the first thing announced should be the label — not "edit text" or "text field" with no context.

Four valid labeling methods, in order of preference:

  1. <label for="id"> — explicit association. The most reliable method. Use for all visible labels.
  2. aria-labelledby="id" — references another element's text. Use when the visible label is elsewhere in the DOM (e.g., a table cell header labeling an input in that row).
  3. aria-label="string" — inline string label. Use only when no visible label is practical (e.g., icon-only search input).
  4. title attribute — tooltip label. Avoid for primary labeling; it is not reliably announced on mobile screen readers.
<!-- Explicit label (preferred) -->
<label for="email">Email address</label>
<input type="email" id="email" name="email" autocomplete="email">

<!-- Visually hidden label (when design has no visible label) -->
<label for="search" class="sr-only">Search guides</label>
<input type="search" id="search" name="q" placeholder="e.g. keyboard testing">

<!-- aria-label (icon-only input, last resort) -->
<input type="search" aria-label="Search guides" name="q">

<!-- aria-labelledby referencing multiple elements -->
<label id="qty-label">Quantity</label>
<input type="number" aria-labelledby="qty-label product-name" min="1" max="10">

Grouping Related Controls

Radio buttons, checkboxes, and any group of related inputs must be grouped with <fieldset> and <legend>. The legend provides the group label that screen readers announce before each individual option, giving users context.

<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="post"> Post
  </label>
</fieldset>

<!-- Screen reader announces:
"Preferred contact method (group)"
"Email, radio button, not checked"
"Phone, radio button, not checked"
"Post, radio button, not checked" -->

Error Handling: The Three Rules

WCAG 3.3.1 (Error Identification) requires errors to be described to the user in text. WCAG 3.3.3 (Error Suggestion) requires that fix suggestions be provided when known. Three rules make errors accessible:

  1. Identify the field in error by name, not just by color. "The Email field is required" — not just a red border.
  2. Describe the error. "Please enter a valid email address" — not just "invalid input".
  3. Associate the error message with the field programmatically so screen readers announce it when the field is focused.
<!-- Accessible error message pattern -->
<label for="email">Email address</label>
<input
  type="email"
  id="email"
  name="email"
  aria-invalid="true"
  aria-describedby="email-error"
  required
>
<span id="email-error" role="alert">
  Please enter a valid email address (e.g. name@example.com).
</span>

<!-- Error summary at top of form (for multi-field forms) -->
<div role="alert" aria-labelledby="error-heading">
  <h2 id="error-heading">3 errors prevented this form from being submitted</h2>
  <ul>
    <li><a href="#email">Email: Please enter a valid email address.</a></li>
    <li><a href="#phone">Phone: Please enter digits only.</a></li>
    <li><a href="#terms">Terms: You must accept the terms to continue.</a></li>
  </ul>
</div>

Autocomplete Attributes

WCAG 1.3.5 (Identify Input Purpose) requires that inputs collecting personal information have the correct autocomplete attribute value. This allows browsers to autofill the form, which benefits users with motor disabilities, cognitive disabilities, and dyslexia who struggle with typing.

<input type="text" name="fname" autocomplete="given-name">
<input type="text" name="lname" autocomplete="family-name">
<input type="email" name="email" autocomplete="email">
<input type="tel" name="phone" autocomplete="tel">
<input type="text" name="address1" autocomplete="address-line1">
<input type="text" name="city" autocomplete="address-level2">
<input type="text" name="postcode" autocomplete="postal-code">
<input type="text" name="country" autocomplete="country-name">
<input type="password" name="new-password" autocomplete="new-password">
<input type="password" name="current-password" autocomplete="current-password">
<input type="text" name="cc-name" autocomplete="cc-name">
<input type="text" name="cc-number" autocomplete="cc-number">
<input type="text" name="cc-exp" autocomplete="cc-exp">

Required Fields

Indicate required fields clearly and programmatically. HTML required attribute ensures native browser validation and screen reader announcement. Supplement with a visible indicator (asterisk with legend, or "Required" text):

<!-- At the top of the form -->
<p>Fields marked with <span aria-hidden="true">*</span><span class="sr-only">an asterisk</span> are required.</p>

<!-- Required field -->
<label for="full-name">
  Full name <span aria-hidden="true">*</span>
</label>
<input
  type="text"
  id="full-name"
  name="fullname"
  required
  autocomplete="name"
>

Complex Inputs: Date Pickers and File Uploads

Native date inputs (<input type="date">) have limited styling but are fully accessible without JavaScript. If you must use a custom date picker, it is one of the most complex ARIA patterns — refer to the W3C ARIA Authoring Practices "Dialog (Date Picker)" pattern.

File upload inputs should be labeled and accompanied by instructions about accepted formats and size limits:

<label for="cv-upload">Upload your CV (PDF or Word, max 5MB)</label>
<input
  type="file"
  id="cv-upload"
  name="cv"
  accept=".pdf,.doc,.docx"
  aria-describedby="cv-hint"
>
<p id="cv-hint">Accepted formats: PDF, DOC, DOCX. Maximum file size: 5MB.</p>

Resources