Skip to Content

Overview

Forms are one of the most common sources of accessibility failures. Screen reader users rely on properly associated labels to understand what each field expects. Without labels, a user hears only “edit text” with no context. Error messages must also be programmatically associated with the field that failed.

WCAG Criteria:

Key requirements:

  • Every input must have a <label> associated via for/id
  • Required fields must use aria-required="true" (or the required attribute)
  • Hint text should be linked via aria-describedby
  • Error messages must be associated with the invalid field via aria-describedby and aria-invalid="true"
  • Use <button type="submit">, not a <div> or <a>

Labeled Fields

Labeled Fields vs. Placeholder-Only

Inaccessible
<!-- Placeholders disappear when user types — no persistent label --> <input placeholder="Full name"> <input placeholder="Email address"> <div class="btn">Sign Up</div>
Live Preview
Sign Up
Accessible
<form> <div> <label for="forms-good-name">Full name</label> <input id="forms-good-name" type="text" required aria-required="true" autocomplete="name" /> </div> <div> <label for="forms-good-email">Email address</label> <input id="forms-good-email" type="email" required aria-required="true" aria-describedby="forms-good-email-hint" autocomplete="email" /> <span id="forms-good-email-hint">We'll send a confirmation to this address.</span> </div> <button type="submit">Sign Up</button> </form>
Live Preview
We'll send a confirmation to this address.

What’s wrong with placeholder-only inputs?

  • Placeholders are not visible once the user starts typing
  • Screen readers may not announce placeholder text as the field’s label
  • The <div> button is not keyboard accessible and has no button role

What the screen reader announces:

VersionAnnouncement on focus
Inaccessible (placeholder)“edit text” or “blank” — no label
Accessible (label)“Full name, required, edit text”
With hint”Email address, required, edit text — We’ll send a confirmation to this address.”

Error Handling

Associated Error Messages

Inaccessible
<!-- Error text exists visually but is not linked to the field --> <label for="forms-bad-pw">Password</label> <input id="forms-bad-pw" type="password" style="border-color:red"> <p style="color:red">Must be at least 8 characters.</p>
Live Preview

Must be at least 8 characters.

Accessible
<label for="forms-good-pw">Password</label> <input id="forms-good-pw" type="password" aria-invalid="true" aria-describedby="forms-pw-error" /> <span id="forms-pw-error" role="alert" style="color:red"> Must be at least 8 characters. </span>
Live Preview
Error: Must be at least 8 characters.

What’s wrong without aria-invalid and aria-describedby?

  • The red border is a visual-only cue — not perceivable by screen reader users
  • The error paragraph is nearby in the DOM but not programmatically linked to the field
  • When the user focuses the input, the error is never read

What the screen reader announces:

VersionAnnouncement on focus
Inaccessible”Password, edit text” — error never mentioned
Accessible”Password, invalid entry, edit text — Must be at least 8 characters.”

Resources