Labels: The Most Important Element in a Form

A label names a field for every reader and grows its click target. Two ways to bind one, and the reasons a form without labels is not usable.

Concept

A label attaches a name to a form control. That sounds minor and is not. Without a label a control has no accessible name, which means a screen reader announces edit text, blank and the reader has no idea what to type.

A correctly bound label does three things:

  • Gives the control its accessible name, announced whenever the control receives focus.
  • Makes the label text itself clickable, focusing the field or toggling the checkbox. On a phone this turns a twenty pixel checkbox into a target the width of a sentence.
  • Tells the browser what the field holds, which improves autofill.

Two ways to bind

Explicit - for and id

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

The for attribute holds the id of the control. This is the preferred form: the two elements can sit anywhere relative to each other, so any layout is possible.

Implicit - wrapping

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

No ids needed, which is convenient in generated markup. The limitation is layout: the control must sit inside the label. It is most useful for checkboxes and radio buttons, where the text follows the control anyway.

Using both together - a wrapping label that also carries for - is fine, and a good safety net in templates.

Example

<form action="/register" method="post">
  <p>
    <label for="name">Full name</label>
    <input type="text" id="name" name="name" required autocomplete="name">
  </p>

  <p>
    <label for="password">Password</label>
    <input type="password" id="password" name="password"
           required minlength="12"
           autocomplete="new-password"
           aria-describedby="password-help">
    <small id="password-help">At least twelve characters. A passphrase works well.</small>
  </p>

  <p>
    <label>
      <input type="checkbox" name="updates" value="yes">
      Send me occasional course updates
    </label>
  </p>
</form>

Explanation

The first two fields use explicit binding so the label can sit above the input. The password field adds aria-describedby, which points at the hint: the label is announced as the name, then the hint is announced as a description. Putting the hint inside the label would make the accessible name a whole sentence, read out every single time.

The checkbox uses implicit binding, because the text naturally follows the control and the whole phrase becomes the click target.

Marking a field required

<label for="email">
  Email address <span aria-hidden="true">*</span>
</label>
<input type="email" id="email" name="email" required>

<p><small>Fields marked * are required.</small></p>

The asterisk is hidden from screen readers because required is already announced. Without aria-hidden the field is announced as Email address star, required.

Some designs mark optional fields instead, which is clearer when most fields are required. Either way, explain the convention once at the top of the form.

Never hide a label with display none

If the design has no room for a visible label, the label must still exist for assistive technology. There are three correct approaches and one wrong one.

<!-- WRONG: removes it from the accessibility tree too -->
<label for="q" style="display:none">Search</label>

<!-- Correct 1: visually hidden, still announced -->
<label for="q" class="visually-hidden">Search the site</label>
<input type="search" id="q" name="q" placeholder="Search">

<!-- Correct 2: aria-label when no visible text exists at all -->
<input type="search" name="q" aria-label="Search the site">

<!-- Correct 3: point at existing visible text -->
<span id="q-label">Search</span>
<input type="search" name="q" aria-labelledby="q-label">
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

Prefer the visually hidden label. It survives translation and works with voice control software, which relies on matching spoken words to visible text.

Important rules

  • for must exactly match the id, and ids are case sensitive.
  • A label may be bound to only one control.
  • A control may be named by several labels, and they are concatenated.
  • Labels do not work with div, span or any non form element.
  • aria-label overrides any visible label text, so use it only when there is no visible text.
  • Legends label a group; labels label a control. They are not interchangeable.

Common mistakes

  • No label at all, relying on a placeholder.
  • for and id that do not match, usually a typo or a case difference. The binding silently fails.
  • Duplicate ids on a page, so the label binds to the first one.
  • Hiding a label with display: none.
  • Putting the hint text inside the label, making the accessible name a paragraph.
  • Using a bold span above a field and calling it a label.
  • Labelling a group of radio buttons with a label instead of a legend.

Best practices

  • Every control gets a label. No exceptions.
  • Prefer explicit for and id; use wrapping for checkboxes and radios.
  • Place labels above fields in a single column layout. It is the fastest to complete and works at any width.
  • Put hints in a separate element bound with aria-describedby.
  • Keep label text short and free of instructions.
  • Click every label to confirm it focuses the right control. It is a two second test that catches every broken binding.

Practice

  1. Build a form with five fields, then click each label and confirm focus lands correctly.
  2. Break one for attribute deliberately and note that nothing visibly changes. How would an audit catch it?
  3. Add a hint to a password field with aria-describedby, then move it inside the label and compare the announcements.
  4. Build a search field with a visually hidden label and confirm it is announced.

Useful resources

Hand picked references for this topic
Written by Lorens Mishra

Default administrator account created by the installer.

Continue reading

All HTML notes →

Discussion

0 comments
Sign in to join the discussion.

No comments yet. Be the first to say something.