A well-designed user interface (UI) should clearly identify important content and controls. Often people correlate this to using prominent visual cues to help guide individuals through a task or point them to necessary information. However, what may be visually apparent to some could be misunderstood or completely passed over by others.
If someone uses assistive technology to navigate an interface, understanding important cues or components could prove to be a different task than looking around the screen for prominent visual indicators. For starters, some people use a screen magnifier to assist with seeing a screen. When magnified, only portions of the entire screen will be visible at once, leaving dynamically revealed descriptions potentially out of sight.
There are also those who cannot see at all. For these people, context provided by visually-dependent associations and dynamically revealed content could be skipped, or misunderstood if not announced with necessary context and helpful information. Others may be able to see the screen, but have difficulty understanding the UI without the aid of a screen reader announcing the content to them (For more information on how people may be using a screen reader, check out WebAim’s Screen Reader Survey).
When building inclusive experiences, developers may need to look beyond the native accessibility of HTML elements and use ARIA attributes to augment the semantics of the HTML language. One such ARIA attribute, aria-describedby
, provides the means to associate an element with the text of another object, or objects, in the document. When properly used, this attribute can provide the necessary information to assist in creating inclusive user experiences.
Using aria-describedby
As noted in Léonie Watson’s Short note on aria-label
, aria-labelledby
, and aria-describedby
, the aria-describedby
attribute is most robustly supported when used with interactive elements (such as buttons, form controls, and links), or elements with a landmark or widget role. In all examples aria-describedby
is meant to provide descriptive information about the element that might otherwise be missed.
For example, a password text field with an explanation of what is valid user input:
<label for="pw">
Password
</label>
<input type="password" id="pw" aria-describedby="pw_desc">
<p id="pw_desc">
Please enter a password you'll immediately forget.
</p>
To interact with a form the Tab key is used to move between form controls (input
s, textarea
, select
, etc.). As paragraphs are not inherently keyboard focusable, a person using a screen reader to navigate the form may completely miss the snarky password description. To help ensure the instructions are not missed, the paragraph is given a unique id
for the aria-describedby
attribute to reference. This will result in the announcement of the snarky content, after the accessible name and role of the password input
, when focused.
An example of when one might need to use aria-describedby
on a non-interactive element would be in the context of a confirmation dialog, where an element within was automatically focused when the dialog loads. The following example shows an alert dialog with the least destructive control automatically focused with the autofocus
attribute.
<div role="alertdialog" aria-labelledby="acc_name" aria-describedby="acc_desc">
<h2 id="acc_name">
Are you sure?
</h2>
<p id="acc_desc">
Once you delete this thing, it's gone forever!
</p>
<button autofocus>
Do not delete
</button>
<button>
Delete
</button>
</div>
For many screen readers, this example will be announced like: “Are you sure? dialog. Once you delete this thing, it’s gone forever! Do not delete, button.”
If aria-describedby
had not been used in this example, a screen reader would have just announced “Are you sure? dialog. Do not delete, button”. It would then be on the screen reader user to navigate around the dialog to find any additional information, negating any benefit of auto-focusing the button in the first place.
Complex descriptions
Sometimes context is indicated by more than a single element. In these scenarios, one of the following methods can be used to point to more descriptive content:
Using multiple id
s
The aria-describedby
attribute can accept multiple space separated id
s to create an announcement. The text of the referenced elements will be announced by the order of the id
s . Consider the following as an example:
<p id="desc_1">
Hide your email address by using a nickname for your account name.
</p>
<!-- ... -->
<label>
Nickname:
<input type="text" aria-describedby="desc_2 desc_1">
</label>
<p id="desc_2">
Do not use @ symbols in nicknames.
</p>
Information as to why a user might want a nickname is provided in one area of the document, and requirements for the nickname text field are provided after the text field.
The nickname input
would be announced like: “Nickname, text field. Do not use @ symbols in nicknames. Hide your email address by using a nickname for your account name.”
Note, the ordering of the id
s is important, as the desc_2
text provides meaningful instructions in how to enter a nickname. desc_1
provides context that may have been passed over, but is not essential in filling out the text field.
Referencing a single wrapper
As an alternative to stringing together multiple id
s, aria-describedby
can reference a single element that contains multiple descriptive objects.
<label>
Password:
<input type="password" aria-describedby="desc_group">
</label>
<div id="desc_group">
<p>A password must include the following:</p>
<ul>
<li>A capital letter.</li>
<li>A number.</li>
<li>A special character.</li>
<li>No fewer than 75 characters.</li>
</ul>
</div>
Pointing to a single id
can be far more sustainable when building reusable components. This is especially true if descriptive content is dynamically generated based on user settings or location. What might be a single line of descriptive text in some instances could be two or more depending on component use, language translations, current state, or other situational factors.
Returning to the use of multiple id
s, when a form control has both descriptive text and an error message, the id
of the error message can be added to the aria-describedby
attribute. For example, aria-describedby="error_msg desc_group"
. Doing this will bridge the gap until the aria-errormessage
attribute is available (hopefully in the not too distant future).
Gotchas when describing content
It should be noted that associating content with aria-describedby
has some quirks, and some best practices that you should be aware of:
- Be concise with descriptions. If your description goes into great detail, or includes a bunch of fluff, it may be difficult for people to follow.
- Any content referenced with
aria-describedby
will be concatenated into a single text string. It’s important to use punctuation, such as periods at the end of a complete thought, so the description won’t be announced as one run on sentence. - If the semantics are important in the descriptive content, note that they will not be conveyed in the
aria-describedby
announcement. For example, images, links and lists won’t be announced as such. Only the `alt` attribute of an image will be announced, and only Firefox will announce “bullet” prior to announcing a list item. There will be no announcements made of any links within the descriptive content. aria-describedby
content may not always be announced to users, depending on their screen reader and navigation method. The attribute is well supported, but that doesn’t mean there aren’t some things to be aware of:aria-describedby
content may not be announced by all screen readers if navigating to a button, link, or form control with the virtual cursor. JAWS specifically may not announce an element’s description when using hot keys to navigate to certain elements. When navigating by visited links, the description will not be announced. However JAWS should announce descriptions when navigating by form controls.- JAWS 17 + IE won’t announce
aria-describedby
content when tabbing to a link (newer version of JAWS have fixed this). - IE11 won’t announce the accessible name or description of a form control if the `title` attribute is used in tandem with
aria-describedby
, and the user is navigating by virtual cursor or form control hot key (F). Both will be announced if using the Tab key. (IE11 had much bigger problems witharia-describedby
in the past.) - TalkBack + Android Chrome won’t announce any
aria-describedby
content of a modal dialog when auto-focusing an element within that dialog. - If a user has descriptions or hint text turned off, any associated content won’t be announced. Because of this, it’s vital that any content that is necessary to the understanding of a UI be available by means other than just
aria-describedby
.
One last notable issue occurs when referencing a single container element with aria-describedby
, rather than referencing multiple elements by their id
s. A single id
is definitely easier to maintain than stringing together multiple, but certain screen readers won’t always announce all the content nested within the referenced container.
For instance, VoiceOver on both desktop and mobile will ignore announcing nested lists from a container element. Returning to the complex password example from earlier…
<label>
Password:
<input type="password" aria-describedby="desc_group">
</label>
<div id="desc_group">
<p>A password must include the following:</p>
<ul>
<li>A capital letter.</li>
<li>A number.</li>
<li>A special character.</li>
<li>No fewer than 75 characters.</li>
</ul>
</div>
VoiceOver will announce “A password must include the following:” as the description to the password input, leaving out all the listed requirements.
Further testing shows other types of nested elements may be ignored as well. For instance, both JAWS and NVDA have issues with `figure` patterns.
<div id="dialog" role="dialog" hidden aria-labelledby="d_head" aria-describedby="instructions">
<h1 id="d_head">Enter code</h1>
<div id="instructions">
<p>The security number for your credit card was missing when checking out.</p>
<figure>
<img src="credit-card-security-location.jpg" alt="security code shown on the right back side of credit card."
<figcaption>
Enter the number as shown.
</figcaption>
</figure>
</div>
<div>
<label>
Security Number
<input id="focus">
</label>
</div>
<button>
OK
</button>
</div>
JAWS 2018 and NVDA 2018.2.1 with Firefox & Chrome will completely disregard the contents of the figure
when the dialog is revealed, and the input
is auto-focused. The description will only include the contents from the paragraph.
Wrapping up
aria-describedby
is an important tool to help provide information where it might otherwise be missed. However, the attribute should rarely be the sole manner in which information is communicated, as doing so will exclude people not using screen readers from the descriptive content. The best way to use aria-describedby
is as an alternate way to provide quick access to descriptions that are accessible by other means.
As with any tool, ARIA attributes, including aria-describedby
are best used appropriately and with care. Doing so, in tandem with working around the few (but important) gaps in support, can help provide inclusive user experiences that are understandable to all.
Comments
Great article! Do you typically recommend using aria-describedby to provide an accessible description for a table or grid (in addition to using aria-labelledby for the accessible name)? We’ve been starting to implement that but unfortunately it does not seem to be well accessibility supported in Internet Explorer and Edge.
Elizabeth, I am not Scott, but he has said I can answer this because I am holding him down right now and he has no choice.
The
element can provide you with a programmatic accessible name that is more broadly supported across older browser and screen reader combinations and still supports sighted users. You do not need
aria-describedby
in that context. There is a WCAG technique for it as well (H39: Using caption elements to associate data table captions with data tables).If your goal is a bit more complex, such as it lives in a
or you are trying to associate it with a heading, then you can benefit from using
aria-describedby
to create those relationships.This is an excellent article on proper aria-describedby usage. Well done! I’ve one “gotcha” to add that I’ve seen a few times in the wild…
If the referenced element is visually “hidden” using display:none, aria-hidden=”true”, or most any other method, it will still be read on the referencing element when using aria-describedby or aria-labelledby. A common use case for aria-describedby is for associating form error messages to controls. If the error message is present in the page and referenced via aria-describedby, but is hidden from view until the error occurs, it will still be read by the screen reader in all cases. This creates a very poor experience for them to hear error messages that aren’t accurate or visible.
A solution to this is to not set the aria-describedby attribute or values until the referenced element is visible. An alternative is to keep the error element empty and populate it with the error text when it appears. In previous testing this latter approach did cause some screen readers to pause very briefly as they attempted to read the empty description.
Excellent article! This is lot of information to absorb & use.
Scott,
Link to ” Short note on aria-label, aria-labelledby” should be https://developertpgi.wpengine.com/blog/2017/07/short-note-on-aria-label-aria-labelledby-and-aria-describedby/
(link is from “augment the semantics of the HTML language”)
Cheers!
Jake
Thanks for the catch, Jake. Fixed 🙂
Hello, I’m working on a job portal application, where we have some radio groups with lengthy legend text (close to 500 characters). Currently we are using role=”radiogroup” with aria-labelledby pointing to the lengthy question. Unfortunately its getting truncated while JAWS reads it and also through JAWS short cut keys. Is there a character length limit on the aria-labelledby text? I tried aria-describedby but because that’s not the accessible name but rather the accessible description, its not showing up through JAWS shortcut key for the radios. current set up:
lengthy question
yes
no
Is there any workaround as we really need the screen readers users to hear the full question so they’d be able to answer the question correctly. JAWS 2018 is the screen reader used for our testing. Thanks much.