Describing aria-describedby

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 (inputs, 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 ids

The aria-describedby attribute can accept multiple space separated ids to create an announcement. The text of the referenced elements will be announced by the order of the ids . 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 ids 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 ids, 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 ids, 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 with aria-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 ids. 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.

Like to be notified about more articles like this? Subscribe to the Knowledge Center Newsletter. It not only gives you summaries and links to our technical blog posts but also TPGi webinars, podcasts, and business blog posts – as well as accessibility and web tech conferences and other events, and a reading list of other relevant articles. You get one email a month, it’s free, requires just your email address, and we promise we won’t share that with anyone. Check the archive.
Categories: Technical

About Scott O'Hara

Scott joined TPGi in 2017 (until 2021), bringing with him nearly two decades of experience working as a designer and front-developer for product companies and UX consulting agencies.

Comments

Elizabeth Whitmer says:

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.

Jared Smith says:

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.

Digital A11Y says:

Excellent article! This is lot of information to absorb & use.

Jake Abma says:

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

Scott O'Hara says:

Thanks for the catch, Jake. Fixed 🙂

Anu says:

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.