Form elements require programmatic labels for WCAG 2.2
Last updated:
Related Guides
Form elements must not be missing programmatic labels
Every interactive form control needs an accessible, programmatic label. This applies to text inputs, selects, textareas, radios, and checkboxes, across desktop and mobile. Missing labels hinder screen reader users and shrink the effective hit area for people with motor disabilities.
Why It Matters
Without a programmatic label, assistive technologies announce controls as “edit” or “button” without context, making data entry guesswork. This increases errors and form abandonment.
Clickable labels also expand the activation area for checkboxes and radio buttons, supporting users with limited dexterity. Clear labels reduce cognitive load by telling users exactly what to enter and where.
Relevant WCAG 2.2 success criteria include 1.3.1 (Info and Relationships), 3.3.2 (Labels or Instructions), and 4.1.2 (Name, Role, Value).
Common Causes
- Missing
<label>element or no association between label and control. - Mismatched for/id values or duplicated ids on the page.
- Relying on placeholder text as the only label.
- Icon-only fields (e.g., search) with no accessible name.
- Using ARIA attributes incorrectly (e.g., wrong or non-existent references).
- Custom form widgets without an accessible name.
How to Fix
- Prefer label + for
- Give each control a unique id.
- Associate a visible label using <label for="..."></label>.
- Allow implicit labels when simple
- Wrapping the input in a
<label>element also creates an association.
- Wrapping the input in a
- Use
aria-labelonly when a visible label is not practical- Suitable for icon-only fields (e.g., a header search). Keep it concise and descriptive.
- Use
aria-labelledbywhen the label text is elsewhere or needs multiple pieces of text- Reference one or more element ids that together form the accessible name.
- Group related choices
- For radio groups or checkbox sets, use
<fieldset>and<legend>for the group label. Each option still needs its own<label>.
- For radio groups or checkbox sets, use
- Keep ids unique
- Do not reuse id values on the same page; screen readers depend on exact matches.
- Avoid placeholder-only labeling (recommendation)
- Placeholder may disappear on input and is not a reliable label. Keep a persistent visible label.
- Know the exceptions
- Buttons are self-labeling via their text or value.
- Inputs with
type="hidden"do not need labels.
How to Test
Keyboard
- Tab through the form: focus moves to each control in a logical order.
- Pressing the label for a checkbox/radio toggles the control.
Screen reader
- With NVDA/JAWS/VoiceOver, navigate to each field: the label text is announced before the control type.
- Check radio/checkbox groups: the legend is announced for the group, and each option’s label is read.
Mobile/touch
- Tapping a label focuses the input or toggles the checkbox/radio.
- VoiceOver/TalkBack announces meaningful names for all controls.
Quick checklist
- Every form control has a programmatic label.
- No id/for mismatches; ids are unique.
- No placeholder-only labels.
- Icon-only controls have
aria-labeloraria-labelledby. - Radio/checkbox groups use fieldset/legend plus individual labels.
Good Example
<form>
<div>
<label for="first-name">First name</label>
<input id="first-name" name="first-name" type="text">
</div>
<fieldset>
<legend>Contact method</legend>
<div>
<input id="contact-email" type="radio" name="contact" value="email">
<label for="contact-email">Email</label>
</div>
<div>
<input id="contact-phone" type="radio" name="contact" value="phone">
<label for="contact-phone">Phone</label>
</div>
</fieldset>
<div>
<input id="site-search" type="search" aria-label="Search site">
</div>
</form>Bad Example
<form>
<div>
First name
<input type="text" name="firstName"> <!-- no label -->
</div>
<div>
<input id="choice1" type="checkbox"> Option A <!-- label not associated -->
</div>
<div>
<input type="search" placeholder="Search"> <!-- placeholder only -->
</div>
<div>
<label for="email">Email</label>
<input id="email-address" type="email"> <!-- for/id mismatch -->
</div>
</form>Quick Checklist
- Each input, select, and textarea has a programmatic label.
- Use
<label for>with unique ids or wrap the input for implicit labeling. - Use
aria-labelonly when a visible label cannot be shown. - Use
aria-labelledbyto combine multiple text nodes into one name. - Group radios/checkboxes with fieldset/legend; label each option.
- Do not rely on placeholders as the only label.
- Verify label/for pairs and id uniqueness across the page.