Skip to main content

Testing Guides

Accessible Forms: Labels, Errors & Validation

46% of home pages have form inputs without programmatically associated labels, according to the WebAIM Million study. Every form control — text input, select, checkbox, radio button, textarea — needs a label that assistive technology can programmatically identify. Without one, screen reader users hear "edit text, blank" with no indication of what information is expected.

Explicit Labels (Preferred)

The most reliable approach: a <label> element with a for attribute matching the input's id. This works across all browsers and assistive technologies, and it increases the clickable area for the input.

<label for="email">Email address</label>
<input type="email" id="email" name="email" autocomplete="email" />

Implicit Labels

Wrapping the input inside a <label> element creates an implicit association. No for or id needed. This is convenient but slightly less robust — some older assistive technology handles it inconsistently.

<label>
  Email address
  <input type="email" name="email" autocomplete="email" />
</label>

Why Placeholder Is Not a Label

Placeholder text is not a substitute for a label. It fails on multiple counts:

  • Disappears on input — Once the user starts typing, the placeholder vanishes. There is no way to recall what was expected.
  • Low contrast by default — Browser default placeholder text typically fails WCAG contrast requirements.
  • Not universally announced — Some screen readers read placeholder text, others do not. It is not a reliable accessible name.
  • Confusing for cognitive disabilities — Users may mistake placeholder text for pre-filled data.

Error Identification (WCAG 3.3.1)

When a form validation error occurs, the error must be identified in text and associated with the relevant input. Do not rely solely on color (a red border) to indicate an error — this fails for color-blind users and violates WCAG SC 1.4.1.

<label for="email">Email address</label>
<input
  type="email"
  id="email"
  name="email"
  aria-invalid="true"
  aria-describedby="email-error"
/>
<p id="email-error" role="alert" class="text-red-600">
  Enter a valid email address, e.g. name@example.com
</p>

The aria-invalid attribute tells assistive technology that the field has an error. aria-describedby links the input to the error message so screen readers announce it in context.

Error Suggestion (WCAG 3.3.3)

Beyond identifying the error, you should suggest how to fix it. Instead of "Invalid input", write "Enter a valid email address, e.g. name@example.com". Specific, actionable error messages reduce cognitive load for all users and are essential for users with cognitive disabilities.

Input Purpose (WCAG 1.3.5)

For form fields that collect personal information, WCAG 1.3.5 (Level AA) requires the autocomplete attribute to identify the input's purpose. This allows browsers and assistive technology to auto-fill fields and display familiar icons.

<input type="text" autocomplete="given-name" />
<input type="text" autocomplete="family-name" />
<input type="email" autocomplete="email" />
<input type="tel" autocomplete="tel" />
<input type="text" autocomplete="street-address" />
<input type="text" autocomplete="postal-code" />

ARIA Patterns for Custom Controls

When native HTML elements are not an option (custom dropdowns, toggle switches, comboboxes), use ARIA to provide accessible names and descriptions:

  • aria-label — Provides an accessible name when there is no visible label text. Use sparingly; visible labels are almost always better.
  • aria-labelledby — Points to the ID of a visible element that serves as the label. Preferred over aria-label when a visible label exists.
  • aria-describedby — Links to supplementary information (help text, character counts, error messages). Read after the label and role.

The first rule of ARIA: use native HTML whenever possible. A <select> with a <label> is always more accessible than a custom dropdown with ARIA roles. Use ARIA only when native semantics fall short of your interaction design, and test thoroughly with a screen reader.

Complete Example: Accessible Sign-Up Form

<form novalidate>
  <div>
    <label for="name">Full name</label>
    <input
      type="text"
      id="name"
      name="name"
      autocomplete="name"
      required
    />
  </div>

  <div>
    <label for="signup-email">Email address</label>
    <input
      type="email"
      id="signup-email"
      name="email"
      autocomplete="email"
      required
      aria-invalid="true"
      aria-describedby="signup-email-error"
    />
    <p id="signup-email-error" role="alert">
      Enter a valid email address, e.g. name@example.com
    </p>
  </div>

  <div>
    <label for="password">Password</label>
    <input
      type="password"
      id="password"
      name="password"
      autocomplete="new-password"
      required
      aria-describedby="password-hint"
    />
    <p id="password-hint">
      At least 8 characters with one number and one letter.
    </p>
  </div>

  <button type="submit">Create account</button>
</form>

Unlabeled form fields are one of the most common automated scan findings. Run a free SweepHound scan to find scanned form controls with missing or broken labels.