Labels Required

Late last year we completed an accessibility audit with a client, during which we were reminded that form fields must have properly associated label elements to be universally understood by screen readers and conventional browsers. We generally use label to mark up form fields, but occassionally use a few other valid ways to identify a field — ways that we felt confident were screen reader friendly, like aria-label. So why should we use a label in all cases? I decided to dig into why and how we should be using label instead of relying on elements or attributes with similar, but limited, qualities.

(If you need a refresher on label markup, MDN has a good form structure tutorial.)

What’s so special about the label?

Permalink to 'What’s so special about the label?'

Using the label element checks all of the boxes for inclusivity when properly formatted: it’s directly associated with a form element (either with the for attribute or by nesting the element inside the label), displayed as text by standard browsers, and spoken by screen readers. Clicking or tapping on a label will focus the associated element (or select the checkbox or radio option), and when you focus directly on the element, screen readers will read the label. Ensuring that both visual and auditory cues are present and intelligible are critical when you consider that in WebAIM’s annual screen reader study screen reader users report relying on a mix of both, with the majority using audio exclusively.

It’s possible to use markup other than label, but when you do, users may see or hear blank form elements and have no idea what to enter. To illustrate, I tested the following inputs — one using a div, one with a label — in 3 screen readers, all with default settings: NVDA 2018 (Windows), VoiceOver (Mac), and VoiceOver (iOS). I kept the style and markup simple, for science:

In our test page, they're visually identical
<div>Email <input class="form-control" type="email"></div>

<label>Email <input class="form-control" type="email"></label>

VoiceOver reads both examples the same way: “Email, edit text” (Mac) or “Email, text field” (iOS). Despite that div has no meaningful semantics, VoiceOver interprets the “Email” text as a label.

NVDA, on the other hand, treats them differently. It ignores the div example’s text and reads “edit, blank”; when it encounters the input with label, it reads “Email, edit, blank”. (It’s probably worth noting that NVDA is ranked by WebAIM’s survey as the second most popular screen reader; VoiceOver is a distant third.)

During this test I also learned:

  • None of these screen readers identify the specified “email” type value. They translate HTML5 input types like “email” and “search” into “text”; any numeric or date inputs are read as “stepper” (VoiceOver) or “spin button” (NVDA). One exception: VoiceOver on iOS will identify a “search field”, but no other types. Generally, we can’t rely on the type attribute to inform screen readers, just as we can’t rely on it to render uniformly across conventional browsers.
  • When label elements are used without text, some screen readers can identify the wrong element, so proper formatting with text is key.

But there are cases where we want to hide label text so that more form elements fit on the screen, or when we want to avoid redundant field identifiers. It’s fairly common to see placeholder or aria-label attributes used as de facto labels when real estate is scarce and hiding extra text from view makes sense (see: most search fields and login forms). Let’s see how they stack up.

The placeholder attribute

Permalink to 'The placeholder attribute'

At first glance, placeholder does a very similar job to label: it’s displayed as text (within text inputs, specifically) and read aloud by screen readers. As an attribute of the text input, there’s no mistaking which field it belongs to.

Formatting hints help reduce errors.
<label>
	<span class="label-text">PIN</span>
	<input class="form-control" type="text" placeholder="4-digit number">
</label>

The placeholder has a purpose, though, that diverges from that of a label. It’s meant to be a suggestion, formatting guideline, or hint that is ultimately replaced by user input. So while a placeholder may seem like a worthy label alternate on the first pass through a form, once a data value is entered, that label is gone. And that’s just one of several reasons why placeholder is an inadequate substitute label. It’s much more valuable when used as a guide.

The aria-label attribute

Permalink to 'The aria-label attribute'

The aria-label attribute provides label text to screen readers for identifying interactive elements, or elements with ARIA roles, and it produces the same audio feedback as the label element. There’s no reason to use both on the same form field, and we actually recommend against trying. Out of curiousity, I tested both on the same field and each screen reader read the combination of label, aria-label, and placeholder text differently: NVDA omits the placeholder, VoiceOver (Mac) omits the label text, and VoiceOver (iOS) omits the aria-label. It’s best to choose a single labelling method.

To that end, consider that aria-label is limited in 2 important ways when it’s used in place of a standard label:

  • it doesn’t display any text, so you’d need to use a separate text element for sighted users. I’ve seen placeholder text or default values used with aria-label to provide the visual piece, but as noted earlier, these values are intended to change with user input.
  • more imporantly, the standard implementation of ARIA attributes continues to be a work in progress for both conventional browsers and screen readers.

Given what label can do, relying on aria-label plus text seems like a long way around the barn (looking at you, Google apps), and one that may not pass the inclusivity test for reaching the widest range of users. Like placeholder, aria-label doesn’t quite stand up to the robustness of label markup, so we stick to using label exclusively. But it can be very useful in other ways, like clarifying duplicate ARIA roles.

The problem these attributes are often used to solve — saving space — can also be accomplished using label markup. You can easily hide labels and still ensure your form is accessible by using styles that keep them available to screen readers.

Know when to (not) show 'em (labels)

Permalink to 'Know when to (not) show 'em (labels)'

When we want to hide markup but ensure that screen readers will find it, we use a class called a11y-only. In an earlier post we covered how to hide elements in an accessible way; if you’re not familiar, I recommend reading that first.

The basic idea is to mark up a form element as you normally would, then enclose the label text in an element assigned the a11y-only class, which stashes it away for screen readers. Once hidden, we just need to make sure the element maintains its affordance for sighted users, and we can do that with helpful attributes or icons.

Use multiple visual cues

Permalink to 'Use multiple visual cues'

We often see a global search input in page headers and navigation bars where space is limited:

Is the label necessary?
<label class="form-search">
	<span class="label-text">Search</span>
	<input class="form-control" type="search" placeholder="Enter a term">
</label>
<button class="form-control">Go</button>

Using a search icon and placeholder attribute (hint: “Enter a term”) makes the visible label somewhat redundant, so we can safely hide it from view with our a11y-only class to shrink the footprint:

Still clearly a search field
<label class="form-search">
	<span class="label-text a11y-only">Search</span>
	<input class="form-control" type="search" placeholder="Enter a term">
</label>
<button class="form-control">Go</button>

Visible and accessibly hidden labels are read the same way by screen readers. In both cases you’ll hear:

  • NVDA: Search, Enter a term, edit, blank
  • VO Mac: Search, edit text, Enter a term
  • VO iOS: Search, Enter a term, search field, double tap to edit

Aside: By default, NVDA will speak “autocomplete” when identifying the field unless it’s explicitly turned off. Use the autocomplete attribute to control this feature.

Consolidate labels into a single heading

Permalink to 'Consolidate labels into a single heading'

Sometimes we’ll use 2-3 fields to collect a set of related data, in which case visible label text isn’t necessary for each individual field. Instead we can use a single heading for the set and accessibly hide individual labels from view, just like we did in the previous example. The container markup we use to group these fields can also convey meaning to screen readers.

One label (heading) is sufficient

When you use fieldset and legend, NVDA and VoiceOver (iOS only) read the legend before the first field’s label. So the following markup:

<fieldset>
	<legend>Birthday</legend>

	<label><span class="a11y-only">Day</span>
		<select class="form-control">
			<option disabled selected>Day</option>
			<option>1</option>
			...
			<option>31</option>
		</select>
	</label>

	<label><span class="a11y-only">Month</span>
		<select class="form-control">
			<option aria-hidden="true" aria-role="presentation">Month</option>
			<option>January</option>
			...
			<option>December</option>
		</select>
	</label>
</fieldset>

Is read aloud as:

  • NVDA: Birthday grouping; Day, combobox, [value], collapsed
  • VO iOS: Birthday, form start; Day, [value], popup button, double tap to activate the picker

(VoiceOver on Mac reads the legend as a typical text node.)

Alternately, you could use a div with a heading element to group related form elements, but there are pros and cons that you’d need to weigh against your goals. With a div + heading you’d lose the bonus “grouping” and “form start” language that screen readers speak when they encounter a fieldset + legend, but when you consider that most screen reader users rely on headings for navigation, using a div + heading may be more helpful structural markup for a multi-part form.

Conclusion: label everything

Permalink to 'Conclusion: label everything'

Out of the box, the label element is all we need to indentify fields in an accessible way. Used in combination with accessible hiding and other helper elements, like placeholder or icons, we can ensure that a form element’s purpose is clearly communicated to all users.

The code that I tested is available on this demo page, and I’ve included markup variations so you can see and hear for yourself how they differ across browsers and screen readers. And if you missed the first post in this (very informal) series, check out Accessible Links Re:visited.

Want to chat about this post? Find us at @filamentgroup on Twitter.

All blog posts