QuestionsAccessibility

Labeling form controls

Accessible FormsEasyAccessibility

How do you correctly label an input? Why isn't a placeholder a label, and what's wrong with a bare aria-label?

What it tests

Programmatic label association and the common placeholder-as-label mistake.

Approach & answer

Every form control needs a programmatically associated label so its accessible name is announced on focus and its hit target includes the label text. The primary tool is <label>: either wrap the control (<label>Email <input></label>) or, better for styling, use for/id (<label for="email">…</label><input id="email">). A correctly associated label also lets sighted users click the label to focus the control — a usability win for everyone. A PLACEHOLDER is not a label: it disappears the moment the user types (so they lose the field's name mid-entry), it's typically low-contrast (a contrast failure), and support for exposing it as a name is inconsistent — never use placeholder as the only label. aria-label and aria-labelledby can supply a name when a visible label genuinely isn't possible (a search field with only an icon), but they have a cost: aria-label has NO visible text, so voice-control users can't say the label to target it, and sighted users get no caption — prefer a real visible <label> and reserve ARIA labelling for icon-only or space-constrained cases. Group related controls (radio sets, a set of checkboxes) in a <fieldset> with a <legend> so the group's purpose is announced. Required, format hints, and errors are conveyed with required/aria-required and aria-describedby, not baked into the placeholder.

Use this technique when

Building any form; killing placeholder-as-label and choosing label vs aria-label.

Code

<!-- Best: visible label associated via for/id -->
<label for="email">Email</label>
<input id="email" type="email" required>

<!-- Also valid: wrapping label -->
<label>Email <input type="email"></label>

<!-- WRONG: placeholder is not a label (vanishes on typing, low contrast) -->
<input placeholder="Email">

<!-- Grouped controls need a fieldset + legend -->
<fieldset>
  <legend>Notify me by</legend>
  <label><input type="checkbox" name="n" value="email"> Email</label>
  <label><input type="checkbox" name="n" value="sms"> SMS</label>
</fieldset>

References