ARIA annotations

WAI-ARIA version 1.3 sees the addition of a set of new features, collectively known as ARIA annotations, which allow the creation of accessible annotations inside web documents. Typical use cases include edit suggestions (i.e. an addition and/or deletion in an editable document), and comments (e.g. an editorial comment related to a part of a document under review).

Below we'll introduce the new features associated with ARIA annotations, and have a look at some code examples that show them in action.

Note: You can find all the examples discussed in this article in a demo file at aria-annotations.

ARIA annotations features

The ARIA attributes providing these new abilities are as follows:

  • aria-description="" — provides a detailed description of an HTML element, as opposed to the brief label provided by aria-label.
  • role="insertion" and role="deletion" — semantically denote HTML elements whose contents represent an insertion to or deletion from the overall document. These are semantically equivalent to the HTML <ins> and <del> elements. Note that these aren’t part of the new ARIA annotations features, but they are of central relevance.
  • role="mark" — semantically denotes HTML elements containing text that is marked/highlighted for reference purposes. This is semantically equivalent to the HTML <mark> element.
  • role="suggestion" — semantically denotes a single proposed change to an editable document. This should be used on an element that wraps a single insertion and deletion (see role="insertion" and role="deletion" above).
  • role="comment" — semantically denotes a comment/reaction to some content on the page, or to a previous comment.

To provide a semantic association between the document content being annotated and the annotation, an aria-details attribute can be set on the annotated content that contains the ID of the annotated element. aria-details has been updated so that it can support multiple IDs — this makes sense, for example you can easily envisage having multiple comments relating to the same bit of text.

ARIA annotation roles and objects are currently exposed in:

  • Firefox from version 75 onwards, on Windows and Linux (on macOS, we are first waiting for Apple to define what Safari will expose as Apple-dialect attributes to VoiceOver, and will then follow suit.)
  • Chrome from version 81 onwards, currently behind the #enable-accessibility-expose-aria-annotations flag (go to chrome://flags to enable this.)

Unfortunately, you won’t be able to use any of these yet, as screenreader support is currently not there. For the moment, you can see the annotations data being exposed with tools like Firefox Accessibility Inspector. The annotations should just work once screenreader support is added.

Associating annotated elements with their details

There are a number of different ways in which you can associate UI features with text labels or descriptions for accessibility purposes. It is useful to know when to use each. You’ll see examples of most of these later on in the article, but briefly:

  • aria-label="" can be set on an element to provide a brief descriptive label when it isn't appropriate to have the label actually appearing in the UI, for example a search input in a horizontal nav bar.
  • aria-labelledby="" can be set on an element and given a value the same as the ID of an element that contains a label for the element. This is useful when the element’s label is available in the UI, but for some reason a conventional <label> won’t work.
  • aria-description="" works the same as aria-label="", but is used when you want to give an element a more detailed description, rather than a short label.
  • aria-describedby="" works the same as aria-labelledby="", but is used when you want to associate the element with a more detailed description, rather than a short label.
  • aria-details="" works in the same way as aria-describedby="", except that it denotes more complex sets of details, rather than simple text descriptions. You can learn more about this in the next section.

aria-details versus aria-describedby

We have already alluded to the difference between these two above — aria-describedby is for textual descriptions, whereas aria-details is for more complex sets of details. But what does this actually mean?

aria-describedby is appropriate for associating an element with a simple text description, where you don’t have much in the way of meaningful semantics contained within. For example:

<p id="description-id">An extended text description of some kind...</p>

<div aria-describedby="description-id">
  <!-- Some kind of UI feature that needs an accessible description  -->
</div>

aria-details is appropriate when linking to descriptions or annotations that are a bit more complex — rather than a simple text string, it might contain multiple bits of semantic information:

<div id="detail-id">
  <h2>A heading</h2>
  <p>An extended text description of some kind…</p>
  <p><time datetime="...">A timestamp</time></p>
</div>

<div aria-details="detail-id">
  <!-- Some kind of UI feature that needs an accessible description  -->
</div>

This difference really becomes apparent when you get to how the content is actually interpreted in the accessibility layer, and read out by screenreaders. Content marked up with aria-describedby is flattened into a simple string, whereas aria-details content is not — so use aria-details if you wat to preserve more complex semantics that go beyond a simple text string.

A basic description

Simple descriptions basically just involve usage of aria-description on an element to provide a description that can’t be gotten from the element’s text alone. As an example, let’s say you have a poll/voting UI widget that shows numbers of votes, but you want to summarize the purpose of the widget in a clear description because the UI does not make it clear:

<section aria-description="Choose your favourite fruit — the fruit with the highest number of votes will be added to the lunch options next week.">
  <p>Pick your favourite fruit:</p>
  <form>
    <ul>
      <li><label>Apple: <input type="radio" name="fruit" value="apple"></label></li>
      <li><label>Orange: <input type="radio" name="fruit" value="orange"></label></li>
      <li><label>Banana: <input type="radio" name="fruit" value="banana"></label></li>
    </ul>
  </form>
</section>

If the descriptive text does appear in the UI, you can link the description to the widget using aria-describedby, like so:

<p id="fruit-desc">Choose your favourite fruit — the fruit with the highest number of votes will be added to the lunch options next week.</p>

<section aria-describedby="fruit-desc">
  <form>
    <ul>
      <li><label>Apple: <input type="radio" name="fruit" value="apple"></label></li>
      <li><label>Orange: <input type="radio" name="fruit" value="orange"></label></li>
      <li><label>Banana: <input type="radio" name="fruit" value="banana"></label></li>
    </ul>
  </form>
</section>

Insertions and deletions

A common wish in online document systems like Google Docs is to be able to track changes, to see what reviewers or editors have suggested as changes to the text, before the managing editor or author accepts or rejects those changes. The semantics for this have long been available in HTML, via the <ins> and <del> elements:

<p>Freida’s pet is a <del>black Cat called Luna</del><ins>purple Tyrannosaurus Rex called Tiny</ins>.</p>

With the new additions, you now have new roles available to provide the same semantics, should you be unable to use <ins> and <del> elements for some reason:

<p>Freida’s pet is a <span role="deletion">black Cat called Luna</span><span role="insertion">purple Tyrannosaurus Rex called Tiny</span>.</p>

However, this often isn’t enough — when you’ve got a content change like the one above that involves an insertion and a deletion, there is no way for a screenreader user to work out if the two are related or not. This is the job of role="suggestion", which should be set on an element wrapping both of them like so:

<p>Freida’s pet is a
  <span role="suggestion"><span role="deletion">black Cat called Luna</span><span role="insertion">purple Tyrannosaurus Rex called Tiny</span></span>.
</p>

We could even provide an information box saying who made the suggestion and when, and associate it with the suggestion via aria-details:

<p>Freida’s pet is a
  <span role="suggestion" aria-details="comment-source"><span role="deletion">black Cat called Luna</span><span role="insertion">purple Tyrannosaurus Rex called Tiny</span></span>.
</p>

<div id="comment-source">Suggested by Chris, <time datetime="2019-03-30T19:29">March 30 2019, 19:29</time></div>

Browsers tend to provide a default black strikethrough for deletions, and a black underline for insertions, but you’ll probably want to use some more interesting styling of your own, for example:

ins, [role="insertion"] {
  color: #0c0;
  text-decoration: underline;
}

del, [role="deletion"] {
  color: red;
  text-decoration: line-through;
}

ins, [role="insertion"], del, [role="deletion"] {
  text-decoration-thickness: 2px;
  background-color: #fee;
  padding: 2px 4px;
  margin: 0 1px;
}

Comments

Online document applications also commonly feature commenting systems, and it would be nice to have a way to semantically associate commented content and its comments. ARIA annotations can help us here too.

Let’s say we have a comment box like so:

<div role="comment" id="thread-1" data-author="chris">
  <h3>Chris said</h3>
  <p class="comment-text">I really think this could use more cowbell.</p>
  <p><time datetime="2019-03-30T19:29">March 30 2019, 19:29</time></p>
</div>

We’ve used role="comment" to mark this up as a comment. To associate the comment with the text being commented, we need to wrap the commented text with an element containing the aria-details attribute, the value of which should be the ID of the comment. <mark> is a suitable element for this purpose (a comment is a reference annotation), so the annotation could look like this:

<p>The last half of the song is a slow-rising crescendo that peaks at the
<mark aria-details="thread-1">end of the guitar solo</mark>, before fading away sharply.</p>

<div role="comment" id="thread-1" data-author="chris">
  <h3>Chris said</h3>
  <p class="comment-text">I really think this moment could use more cowbell.</p>
  <p><time datetime="2019-03-30T19:29">March 30 2019, 19:29</time></p>
</div>

Note: If for some reason you can’t use the <mark> element in your application, you could also use <span role="mark"></span>.

Since aria-details can now accept multiple IDs, we can associate multiple comments with the same annotation, like so:

<p>The last half of the song is a slow-rising crescendo that peaks at the
<mark aria-details="thread-1 thread-2">end of the guitar solo</mark>, before fading away sharply.</p>

<div role="comment" id="thread-1" data-author="chris">
  <h3>Chris said</h3>
  <p class="comment-text">I really think this moment could use more cowbell.</p>
  <p><time datetime="2019-03-30T19:29">March 30 2019, 19:29</time></p>
</div>

<div role="comment" id="thread-2" data-author="chris">
  <h3>Marcus said</h3>
  <p class="comment-text">The guitar solo could do with a touch more chorus,
    and a slightly lower volume.</p>
  <p><time datetime="2019-03-29T15:35">March 29 2019, 15:35</time></p>
</div>

Nested comments are also possible with ARIA annotations — simply nest the comments inside one another, like so:

<div role="comment" id="thread-1" data-author="chris">
  <h3>Chris said</h3>
  <p class="comment-text">I really think this moment could use more cowbell.</p>
  <p><time datetime="2019-03-30T19:29">March 30 2019, 19:29</time></p>

  <div role="comment" data-author="marcus">
    <h3>Marcus replied</h3>
    <p class="comment-text">I don't know about that.
      I think the cowbell could distract from the solo.</p>
    <p><time datetime="2019-03-30T21:02">March 30 2019, 21:02</time></p>
  </div>
</div>