Accessibly labelling interactive elements

All interactive elements should have accessible names, says the W3C about using ARIA. They can also have descriptions. Names and descriptions can help your users figure out which elements they are interacting with, and what they are for. In this post I will explain implicit and explicit labelling methods and discuss how to future-proof your labels.

Accessibility tree

Besides a DOM tree, browsers build an accessibility tree when they render pages. It contains all interactive elements, so that it is easier for assistive technology (AT) to expose a page and its controls to their users. Being aware of how your markup influences the accessibility tree helps a great deal in making your app work for those users.

Interactive elements are things like links, buttons, form inputs and <details> with a <summary> and <audio> with a controls attribute.

An accessibility tree contains accessible objects, which can have these name-related properties:

  • accessible name
  • accessible description

The properties that result in an accessible name are often called label, this is why from the next section I will be calling them labels.

Name vs description

Both the accessible name and the description can provide text alternatives for interactive elements. The difference between the two is that the description is more detailed than the name, and that it is additional, where a name is essential. Generally, it is more useful to provide a name and no description rather than the other way around.

Inspecting the tree

If you want to look at the accessible objects in pages you are building, install the Accessibility Dev Tools in Chrome. With those turned on, go to a website of choice, inspect an element and look at the properties under the Accessibility tab. If you’re on Windows, aViewer lets you browse what accessibility APIs of multiple browsers are exposing.

Labelling with standard HTML

Most of the time, browsers can compute these things from your HTML structure. To make this work, just use standard HTML conventions. With those, your accessible names and descriptions are implicitly exposed.

Labels

This structure, for example, uses a <label>:

<label for="element-1">Phone number</label>
<input type="tel" id="element-1" />

The browser infers the accessible name for the input from the label, so no extra work is required, the input will appear in the accessibility tree with a sensible name.

You can also label an interactive element without rendering the label on screen, with a title attribute:

<audio src="great-song.mp3" title="Great song" controls></audio>

‘Great song’ will be used as this audio fragment’s accessible name, so a user trying to figure out what this audio file is, will be presented with a label.

Descriptions

There are various native ways to associate a description with an element, for example:

  • a <legend> in a <fieldset>
  • a <caption> in a <table>

None of the above are interactive elements, though. This is because for interactive elements, there is no implicit method to add a description without ARIA.

Labelling with ARIA

For some elements there are no labelling methods that use standard HTML. The <label> element, to name one, is just there for form controls. WAI-ARIA allows for more explicit association of labels, with these two properties: aria-labelledby and aria-describedby.

Labels

With the aria-labelledby attribute, you can use another element as the label for your interactive element. This is for cases where no native labelling method is available.

The value of aria-labelledby should be the ID of the element that labels it. You can even add multiple IDs (separated with spaces). Use with care, as in many cases this would cause the label to be unhelpfully long.

Example:

<h2 id="label-lasagna">Lasagna recipe</h2>
<p>To make this lasagna, just follow these 
10 simple steps! <a href="/posts/name-change" 
aria-labelledby="label-lasagna">Read more</p>

“Read more” is not a useful label, as you can imagine what would happen if this component is repeated a couple of times on a given page. A screenreader user tabbing through the links on the page would just hear:

Link Read more Link Read more Link Read more

As the title above the text, I have chosen something that would work well as a label for the link text. I’ve associated it with aria-labelledby. In many screenreaders, a user that tabs over the link will hear ‘Link Lasagna recipe’ instead of ‘Read more’ (PowerMapper have comprehensively tested this technique).

Another example:

<h3 id="label-song-4">4: It never entered my mind</h3>
<p>The fourth song, “It Never Entered My Mind” is a show tune from the 1940 Rodgers and Hart musical Higher and Higher (1940), where it was introduced by Shirley Ross. The Miles Davis recording was used in the movies Runaway Bride (1999) and the Lenny Bruce biopic Lenny (1974).</p>
<audio src="4-mind.mp3" controls aria-labelledby="label-song-4">

This exposes ‘4: It never entered my mind’ as the accessible name of our audio fragment. When a user tabs onto the audio button, a screenreader should read out the title (this is in theory; in practice, support for aria-labelledby on <audio> is actually quite bad).

If, for some reason, your label is not visible on screen and you want to add it for screenreaders only, you can also use aria-label with the actual label text as its value. I would not recommend this in most cases — if a label is helpful, why only show it to some of your users?

Descriptions

The aria-describedby lets you associate an element with another element as its description.

Example:

<input type="checkbox" id="accept-terms" aria-describedby="small-print" />
<label for="accept-terms">Accept our terms</label>
<p id="small-print">Note that upon accepting our terms, you also agree to us calling you with offers for beautiful kitchens, mortgage plans and magazine subscriptions.</p>

The small print text will display, but by associating it with aria-describedby you make it easy for assistive technologies to provide a description for the input to their users.

This can help screenreader users that are tabbing through the form, to make a better choice about whether to accept the terms.

Future-proofing labels and descriptions

Arguably, the best labels and descriptions are the ones that are up to date. The ones that have been added properly and provide useful, sensible text, explaining what a control is for.

This is harder than it may sound. As Mark Pilgrim concludes in his post about longdesc, a theoretical solution does not necessarily provide ease of use to your users. He cites statistics that say that “less than 1% of images that provide a longdesc attribute are actually useful”. Wow!

Risk of breakage

One way less useful labels can make their way into your product, is because of human error or even unintended sloppiness. By yourself, by another team member, by a team member 3 years from now. A developer could add the proper associations now, only for someone later in the process to break it unintentionally.

Maybe one of the content editors forgot that a heading was associated as a button’s label, and changes it into something that no longer labels it. Or it could be that a developer isn’t aware of the latest aria-* attributes and breaks something. Note that such breakages can happen without anyone noticing.

Avoiding breakage

An approach to get most out of your labels in the long run: make sure the labels and descriptions can be seen by everyone on your product team and by all of your users. It then happens where everyone can see it, which helps fixing any breakage as it happens.

Another thing that can help is to have a component based approach. In a component, you would usually associate variables rather than actual values (i.e. aria-labelledby="{{ id }}" instead of aria-labelledby="might-forget-this-is-here".)

Lastly, I would recommend making sure the labelling approach is addressed in documentation and that all testers / reviewers in your team(s) are aware of what the attributes do, so that they can spot any issues before they go live.

In conclusion

To help all users make sense of your page, ensure all interactive elements are properly labeled and have descriptions where appropriate. There are several native techniques to do this, as well as ARIA attributes.

To associate labels with interactive elements, always prefer native methods as they will have the best browser support and there is most chance your users and other developers are familiar with them. If native labelling really is not possible, use aria-labelledby (if label is rendered on screen) or aria-label (if label is not rendered on screen). To associate descriptions with interactive elements, use aria-describedby.

Further reading

Comments

Web Axe 18 May 2017 13:06:36

More commonly, the term implicit labeling is used when the label wraps around the input. Explicit labeling use the For and Id attributes.

Hidde de Vries 18 May 2017 13:17:18

Cheers, some people on Twitter pointed the same out to me. Many thanks!

What I meant is that the accessible names / accessible descriptions are set implicity/explicity. So ‘labelling’ as in providing text alternatives, not ‘labelling’ as in using the `<label>` element. Well, I understand the confusion, have updated the text.

Roel Van gils 22 May 2017 20:44:39

Thanks for the excellent write-up!

One thing I wouldn’t recommend, is using a `title` on an embedded audio file. There’s no sure way to know whether a user will have access to this information. For example, In Jaws, it requires changing a setting for the title to be read, which not all users have enabled. I believe, nowadays, using `aria-label` is a much safer bet than `title`.

In general, I think additional or alternative content for audio should be visible for everyone anyway. The users that will probably benefit the most of it, are those who have no access to the audio (or are deaf or hard of hearing). They will probably never notice the presence of the `title` attribute, especially when they’re using a touch device that has no :hover support.

Leave a comment

Hidde de Vries (@hdv on Twitter) is a freelance front-end developer based in Rotterdam, The Netherlands. He helps governments and businesses get usable front-ends for their end users. Member of Fronteers and former volunteer there, organising conferences, meetups and workshops.