Originally posted on a11ywithlindsey.com. If you'd like to see video examples of how screen readers interpret these, they are at the source!
Last week, I got a decent amount of folks who found the aria states blog post helpful. This week I wanted to continue with that and go over something that took me a long time to understand: labeling. How do you label things? What needs labeling? What is the difference between all the ways to label things?
After this post, you should have a firmer understanding of all things labels.
Form Labels
Lack of form labels is one of the most common places I see accessibility errors. Most people do something like this:
<form>
<input type="search" placeholder="Search" />
</form>
I’m going to repeat it for those in the back:
Placeholder attributes are not enough to label a field for a screen reader.
To properly label this, we need to ensure that there is a <label>
that exists and is also associated with the input. The way to associate it is to give the <input>
element an id
and to match that value to a for
attribute on the label.
<form>
<label for="search">Search</label>
<input type="search" placeholder="Search" id="search" />
</form>
Take note that the string in the for
attribute matches the id
in the input. Matching these strings is how you associate them!
If a designer gives you a mock and there is absolutely no way to tell them to add a label to the design, you’re in luck. You can add a visually-hidden
or sr-only
(screen reader only) class. I always use this CSS from the a11y project:
.visually-hidden {
position: absolute !important;
height: 1px;
width: 1px;
overflow: hidden;
clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
clip: rect(1px, 1px, 1px, 1px);
}
The above code would turn into this:
<form>
<label class="visually-hidden" for="search">Search</label>
<input type="search" placeholder="Search" id="search" />
</form>
Pretty simple, this applies to <select>
elements as well.
Nesting inputs in labels
I’ve also seen people nest their inputs. Nested inputs aren’t my preferred method, but if I do something like this:
<form>
<label>
Search:
<input type="search" />
</label>
</form>
This HTML is valid and doesn’t produce any errors with the Wave tool. However, it makes it more difficult to visually hide the label, if that’s what you need to do. So choose your method based on your needs!
Using aria-label
As a disclaimer, this is my least favorite way to label things for screen readers. I prefer to label things with elements that exist in the markup. However, sometimes, it is necessary. A few times that I’ve found it necessary:
- I had no control of the markup and had to change up the DOM in JavaScript. It’s way easier to
setAttribute()
than it is to create an entire element and append it. - I was working in an SVG that I couldn’t add
<text>
elements to
The way you use an aria-label
is to put the string in the attribute. For example, I found this on the MDN site:
<svg
class="icon icon-github"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="28"
viewBox="0 0 24 28"
aria-label="GitHub"
role="img"
focusable="false"
>
<!-- all the children -->
</svg>
The aria-label attribute value does not need to match the id
of another element.
aria-describedby
and aria-labelledby
aria-describedby
and aria-labelledby
took awhile to demystify because the two attributes sound like they are the same. So let’s break them down into the literal sense.
Labels are like titles. They tell you what something is. When do we announce a title or a label: before the element.
Let’s take the third example from the MDN docs.
<div id="radio_label">My radio label</div>
<ul role="radiogroup" aria-labelledby="radio_label">
<li role="radio">Item #1</li>
<li role="radio">Item #2</li>
<li role="radio">Item #3</li>
</ul>
When we use VoiceOver to navigate to the radio group, it reads “My radio label, radio group?” The radio label got announced before it told us about the radio group.
Let’s take the same markup from above and add an aria-describedby
attribute with a div.
<div id="radio_label">My radio label</div>
<ul
role="radiogroup"
aria-labelledby="radio_label"
aria-describedby="radio_desc"
>
<li role="radio">Item #1</li>
<li role="radio">Item #2</li>
<li role="radio">Item #3</li>
</ul>
<div id="radio_desc">
A bit more about this radio group. Here are some words.
</div>
The text “A bit more about this radio group. Here are some words” happens after VoiceOver announced after the radio group. It’s a very subtle difference where, in most cases, it won’t matter.
The strings that we put in these attributes are like the form labels above. We want the aria-describedby
or aria-labelledby
attribute to match the id
of the element that we want the screen reader to read.
Conclusion
I hope I helped you understand a bit more about the confusing world of labeling for accessibility! Let me know on Twitter or comment below what you thought or if you have any other questions.
Also, I have a patreon! If you like my work, consider making a $1 monthly pledge. You’ll be able to vote on future blog posts if you make a \$5 pledge or higher! Cheers! Have a great week!