Form Labeling
The hierarchy of labeling methods — from native HTML to ARIA fallbacks
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:
- 1.3.1 Info and Relationships — structure must be conveyed programmatically
- 3.3.1 Error Identification — errors must be described in text
- 3.3.2 Labels or Instructions — inputs must have labels
- 4.1.2 Name, Role, Value — interactive elements must expose their role and state
Key requirements:
- Every input must have a
<label>associated viafor/id - Required fields must use
aria-required="true"(or therequiredattribute) - Hint text should be linked via
aria-describedby - Error messages must be associated with the invalid field via
aria-describedbyandaria-invalid="true" - Use
<button type="submit">, not a<div>or<a>
Labeled Fields
Labeled Fields vs. Placeholder-Only
View inaccessible code
<!-- Placeholders disappear when user types — no persistent label -->
<input placeholder="Full name">
<input placeholder="Email address">
<div class="btn">Sign Up</div>View accessible code
<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>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 nobuttonrole
Predicted semantic summary (exact output varies):
| Version | Announcement 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
Must be at least 8 characters.
View inaccessible code
<!-- 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>View accessible code
<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>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
Predicted semantic summary (exact output varies):
| Version | Announcement on focus |
|---|---|
| Inaccessible | ”Password, edit text” — error never mentioned |
| Accessible | ”Password, invalid entry, edit text — Must be at least 8 characters.” |
Labels, instructions, descriptions, and errors
A label identifies a control; it should not be overloaded with every instruction and error. Put format requirements and help in visible text and reference them with aria-describedby. Group related controls with <fieldset> and <legend>. On validation, identify the error in text, set aria-invalid="true", and associate the error with the field. A populated role="alert" in initial markup is not a reliable announcement technique; for dynamic validation, update a pre-existing alert or focus a headed error summary.
The accessible name should contain the visible label text in the same order so speech-recognition users can target what they see (WCAG 2.5.3 Label in Name). Placeholder text is an example or hint, not a persistent label.