Context is king: long live the king!

In a recent post on the TPGi Technical blog, Doug Abrams wrote an article titled Good Intentions, Poor Context. In that article, he covered a number of things that we often see while auditing websites which, while well-intended, actually cause issues for some users. In particular, the issue of providing context—extra information that the developer thought might help the user—which backfires instead.

But context can be a good thing. In fact, it can be a great thing! So in this article I wanted to provide some course-correction to some of those problems that Doug noted. Specifically, I wanted to demonstrate a number of ways that a developer can provide that extra context, safely, and to point out some of the gotchas you need to be aware of. At the end of all this, I also have a little tool that I whipped up which an auditor can use to quickly generate examples of everything that I cover in this article.

Delete, delete, delete

For the purposes of this article, I am going to hone in on one example that we see an awful lot: a control that has a very generic visual label that appears multiple times on a page. Often this is something that has been churned out from a database lookup of some kind. We’ll take a shopping cart example where there are numerous repeated controls, such as a ‘Delete’ button.

Without any further action from the developer, all of these ‘Delete’ controls will be exposed exactly as that to assistive technology users:

Wouldn’t it be more helpful if the ‘Delete’ control indicated clearly what it would delete? Well, yes, it would, the author responded somewhat unnecessarily.

Providing context – Order of preference

As ever, we should try to avoid ARIA attributes as much as possible (remember the first rule of ARIA). With that in mind, this is my preferred order of the techniques that you can use to provide extra context for controls like this:

  1. Have the context provided for all users in the visible label (there will be caveats!)
  2. Use visually-hidden text (CSS Clip method)
  3. Use aria-labelledby
  4. Use aria-label

There are also approaches that do not change the accessible name but provide context through accessible descriptions, which I will cover later. But these four above? They’re your mainstays. Let’s dig in …

1. Have the phrase shown in full for everyone to see

And I’m going to go straight in with the caveat for this one: if the design/layout can accommodate it!

This is the ideal situation for all, if you can swing it. No hidden text, no ambiguity at all:

<button>Delete INIU USB C to USB C Charger Cable</button>
<button>Delete JETech Screen Protector for iPhone SE 3/2 (2022/2020 Edition)</button>
<button>Delete GP Batteries AA batteries AA pack of 40 Ultra Alkaline</button>

But the thing is, I have already painted a much rosier picture than the reality of the situation. Those product descriptions above? Yeah … I edited them for brevity for this article. Let’s see what they would really have looked like in their unedited form:

<button>Delete INIU USB C to USB C Charger Cable, 100W Fast Charging USB C Cable [2-Pack 2m] QC 4.0 PD 5A, Braided Phone Charger Type C Cable for iPhone 15 iPad Air MacBook Pro 2020 Samsung S22 Switch Huawei etc.</button>
<button>Delete JETech Screen Protector for iPhone SE 3/2 (2022/2020 Edition) 4.7-Inch, Tempered Glass Film, 3-Pack</button>
<button>Delete GP Batteries AA batteries AA pack of 40 Ultra Alkaline disposable double aa batteries 1.5v 10 year shelf life for toys fairy lights camera household applications LR6 Basic AA Battery</button>

Now let’s see what they would have looked like to the user if we just took the full description that comes out of the database:

Yeah, that’s not gonna happen is it? This is why we have simple, predictable and easy-to-accommodate button designs. So let’s quickly move on to the next best option.

2. Use visually-hidden text (CSS Clip method)

With this approach, we use visually-hidden text in a child element which contributes to the accessible name of the parent button.

In the CSS you have this:

.visually-hidden {
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    height: 1px;
    overflow: hidden;
    position: absolute;
    white-space: nowrap;
    width: 1px;
}

And then you mark up the buttons like this:

<button>Delete<span class="visually-hidden"> INIU USB C to USB C Charger Cable</span></button>
<button>Delete<span class="visually-hidden"> JETech Screen Protector for iPhone SE 3/2 (2022/2020 Edition)</span></button>
<button>Delete<span class="visually-hidden"> GP Batteries AA batteries AA pack of 40 Ultra Alkaline</span></button>

A screen reader user would perceive it something like this:

  • “Delete INIU USB C to USB C Charger Cable, button”
  • “Delete JETech Screen Protector for iPhone SE 3/2 (2022/2020 Edition), button”
  • “Delete GP Batteries AA batteries AA pack of 40 Ultra Alkaline, button”

You can read all about the weird and wonderful things that make this technique work (if you’re into the nuts and bolts of this sort of thing) in our article The anatomy of visually-hidden.

Another thing to briefly mention. I used the phrase ‘a screen reader user would perceive it something like this’. Something like this? If that seems vague, that’s just because different screen readers announce things in a different order. For example, the control type may be announced before the control’s accessible name in one screen reader, but after it in another.

3. Use aria-labelledby

With this next approach, each button references both itself and one or more other text elements to give context. Assuming that the product name is in an existing element (here it’s an <h2>), you can give that heading an id, then the button references that in addition to its own visible text:

<h2 id="someOtherTextNode1">INIU USB C to USB C Charger Cable</h2>
<h2 id="someOtherTextNode2">JETech Screen Protector for iPhone SE 3/2 (2022/2020 Edition)</h2>
<h2 id="someOtherTextNode3">GP Batteries AA batteries AA pack of 40 Ultra Alkaline</h2>

And here are the buttons:

<button id="thisControl1" aria-labelledby="thisControl1 someOtherTextNode1">Delete</button>
<button id="thisControl2" aria-labelledby="thisControl2 someOtherTextNode2">Delete</button>
<button id="thisControl3" aria-labelledby="thisControl3 someOtherTextNode3">Delete</button>

A screen reader user would perceive it something like this:

  • “Delete INIU USB C to USB C Charger Cable, button”
  • “Delete JETech Screen Protector for iPhone SE 3/2 (2022/2020 Edition), button”
  • “Delete GP Batteries AA batteries AA pack of 40 Ultra Alkaline, button”

4. Use aria-label

In this technique, we are not referencing other visible elements on the page. This can be problematic, though, and it’s why it’s in position four here. More on that later …

<button aria-label="Delete INIU USB C to USB C Charger Cable">Delete</button>
<button aria-label="Delete JETech Screen Protector for iPhone SE 3/2 (2022/2020 Edition)">Delete</button>
<button aria-label="Delete GP Batteries AA batteries AA pack of 40 Ultra Alkaline">Delete</button>

A screen reader user would perceive it something like this:

  • “Delete INIU USB C to USB C Charger Cable, button”
  • “Delete JETech Screen Protector for iPhone SE 3/2 (2022/2020 Edition), button”
  • “Delete GP Batteries AA batteries AA pack of 40 Ultra Alkaline, button”

The issue with this approach is that hidden text which is jammed into aria-label attributes has typically been a problem for online translation services. Text on screen is translated just fine, but the aria-label text escapes translation.

It’s not just on-the-fly translations that can be an issue. Many sites have localization processes such that the same design is deployed in multiple countries/languages. Text content is handled by tokens that receive the appropriate translation depending on the region. That’s great! But I know from personal experience that when text is provided in aria-label attributes, it can easily evade the tokenization process. During audits I’ve seen buttons with German, Thai and Chinese visible labels that have entirely different accessible names due to the un-translated aria-label text. Hello there, SC 2.5.3 Label in Name (Level A) failure!

The end result

If you have used any of these techniques, you will have provided useful context for AT users. Taking a look at how these controls show up in VoiceOver (Rotor > Form controls) we now see this:

Some further tips:

  • Make sure that the visible control name is the first part of the control name. In other words:
    • Incorrect “INIU USB C to USB C Charger Cable – Delete, button”
    • Correct “Delete INIU USB C to USB C Charger Cable, button”
  • If you use aria-label, you MUST ensure that the visible text is in the accessible name, otherwise you’ll fail SC 2.5.3 Label in Name. Taking ‘Save for later’ as an example, while it might seem more natural English to state it as ‘Save THE THING for later’, inserting THE THING in the middle like this means the visible text is no longer in the accessible name:
    • Incorrect <button aria-label="Save INIU USB C to USB C Charger Cable for later">Save for later</button>
    • Correct <button aria-label="Save for later - INIU USB C to USB C Charger Cable">Save for later</button>

Other ways to provide context

The following approaches do not provide context in the accessible name. They may help in some circumstances, though. Some use the aria-describedby attribute to provide extra context. However, bear in mind that the announcement of aria-describedby content is dependent on how the screen reader user is navigating the page. To give an example, a control that recevies focus as a result of a TAB keypress should include the aria-describedby content in the announcement, but it won’t show that context when the user pulls up a list of form controls. As such, this cannot be wholly relied upon. Be sure to read the notes against each technique for more info. Or, if you really want to get into the fine details, Adrian Roselli has documented these differences thoroughly in an article titled Accessible Description Exposure.

5. Use an aria-describedby which references another text element

Like the aria-labelledby example, we’ll assume that the product names are available in headings on the page (with id attributes):

<h2 id="someOtherTextNode1">INIU USB C to USB C Charger Cable</h2>
<h2 id="someOtherTextNode2">JETech Screen Protector for iPhone SE 3/2 (2022/2020 Edition)</h2>
<h2 id="someOtherTextNode3">GP Batteries AA batteries AA pack of 40 Ultra Alkaline</h2>

This time, we use aria-describedby to reference those product names:

<button aria-describedby="someOtherTextNode1">Delete</button>
<button aria-describedby="someOtherTextNode2">Delete</button>
<button aria-describedby="someOtherTextNode3">Delete</button>

A screen reader user would perceive it something like this:

  • “Delete, button” … short pause … “INIU USB C to USB C Charger Cable”
  • “Delete, button” … short pause … “JETech Screen Protector for iPhone SE 3/2 (2022/2020 Edition)”
  • “Delete, button” … short pause … “GP Batteries AA batteries AA pack of 40 Ultra Alkaline”

  • “Delete, button”
  • “Delete, button”
  • “Delete, button”

So it’s not a particularly robust technique.

6. Use an aria-describedby which references a hidden text element

Very similar to the previous example, except that this time we’re referencing text elements that are not visible on the page. You knew that hidden elements could still be referenced and used as a name source, right? Well, you do now:

<div id="someOtherTextNode1" hidden>INIU USB C to USB C Charger Cable</div>
<div id="someOtherTextNode2" hidden>JETech Screen Protector for iPhone SE 3/2 (2022/2020 Edition)</div>
<div id="someOtherTextNode3" hidden>GP Batteries AA batteries AA pack of 40 Ultra Alkaline</div>
...
<button aria-describedby="someOtherTextNode1">Delete</button>
<button aria-describedby="someOtherTextNode2">Delete</button>
<button aria-describedby="someOtherTextNode3">Delete</button>

A screen reader user would perceive it something like this:

  • “Delete, button” … short pause … “INIU USB C to USB C Charger Cable”
  • “Delete, button” … short pause … “JETech Screen Protector for iPhone SE 3/2 (2022/2020 Edition)”
  • “Delete, button” … short pause … “GP Batteries AA batteries AA pack of 40 Ultra Alkaline”

Note: There is a good reason that you might want to use hidden text elements and this would be for providing ‘joining’ words or phrases. Text that has no purpose being on screen but may be used to more elegantly join several name sources together comes to mind.

That’s quite a lot of ways to do this, isn’t it?

Agreed. And as an auditor, providing this advice to clients can be tedious unless you’re just copy/pasting from a standard block of advice. Wouldn’t it be great if you could dynamically generate advice like the above based on the specific examples that you find while auditing? Well, cue some shimmery sound effects as I figuratively wave a magic wand to reveal this:

Button/Link Namer

You can use this tool below to quickly generate advice that, essentially, mirrors the examples shown above (minus any sarcasm or pithy statements). Type a control name (such as ‘Delete’) and then some hidden text (one or more lines that will be repeated) which provide context. Then copy the advice generated. Give it a whirl, see how you go!

For example, repeated buttons all showing as ‘Delete’

This text will be included in the acccessible name but not shown visually. If you want to see the effect of multiple controls with the same visible name but different accessible names, enter multiple contextual phrases.

Link or button?


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 Ian Lloyd

Ian Lloyd (or 'Lloydi' as he prefers) is a Principal Accessibility Engineer at TPGi. He joined the company in 2020 and previously worked for Apple as an accessibility engineer. Prior to that, he spent what seems like a lifetime working for a financial services company, trying to promote accessibility with varying levels of success. In an alternate universe, he continued his early path of DJ-ing and producing to become a superstar DJ, but this universe had other ideas. Outside of work, he maintains a11y-tools ("A random collection of accessibility-focused tools that you might find at least partially useful").

Comments

Jean Ducrot says:

I’ll be sharing this post left, right, and center. Thank you sir!