Skip to main content

aria-label is a code smell

The aria-label property is made available to us by the Accessible Rich Internet Applications (ARIA) standard. It allows a property/value declaration in HTML as a way of providing an accessible name for an interactive element.

Accessible names

Accessible names use text to identify interactive elements on a page. They allow someone using assistive technology to understand, and then take action on those interactive elements.

Keeping accessible names concise and descriptive is one of the most important things when coding things in an accessible way. A great example of an accessible name is the content placed between opening and closing a tags:

<a href="duck-breeds.html">
Duck breeds

Accessible names are computed via an algorithmic process derived from Accessible Name and Description Computation. Here, an algorithm evaluates each DOM node’s text content, attribute content, and associated elements to determine which declaration is the most relevant. This information is then exposed to the browser’s Accessibility Tree.

Assistive technology consumes information generated by the accessibility tree. This in turn enables people using the assistive technology to understand the makeup of a website or web app’s content and take action on it.

Code smell

The term “code smell” refers to small, repeated patterns or one-off, atypical code constructs that indicate something is worth paying attention to. To quote Martin Fowler:

“…smells don't always indicate a problem. […] You have to look deeper to see if there is an underlying problem there—smells aren't inherently bad on their own—they are often an indicator of a problem rather than the problem themselves.”

In my experience, the term code smell usually has a negative connotation when someone mentions it.

It’s a lot like a noticeable smell emanating from the office refrigerator. Ideally it’s some delicious homemade kimchi someone brought in, but more often than not it’s something like some forgotten fish sticks rotting in the back.

To stretch this metaphor to its breaking point, I also don’t think the forgotten food is the result of malice. The person who left the fish sticks might:

aria-label as a code smell

Sadly, I run into aria-label declarations a lot more than I’d expect to. There are probably a whole host of reasons for this, but if I could name some of the more popular factors they may be:

When I encounter too much, or mis-applied aria-label it makes me take notice. This code smell puts me on alert to investigate things more thoroughly, as it most likely indicates accessibility issues.

Before you scoff at this, I’d encourage you to read the WebAIM Million report. This evaluation of the accessibility of the top 1,000,000 homepages revealed:

“Increased ARIA usage on pages correlated to higher detected errors. The more ARIA attributes that were present, the more detected accessibility errors could be expected.”

That’s a big ol’ oof right there.

ARIA has varying levels of support

Web developers are used to thinking of support as a binary yes or no. However, ARIA is a bit unique in that it’s support is more conditional, and what it actually does is highly contingent on:

  1. How it is being used, and
  2. The surrounding context of the rest of the overall experience.

Determining actual ARIA support is a bit more complicated than other web technologies, in that it relies on the:

Sadly, you can’t take a browser, operating system, or assistive technology manufacturer’s word at good faith. Determining support means performing manual tests to determine what assistive technology actually reports.

What’s the difference between text content and attribute content?

Before we get any further into it, I think it is important to outline the difference between text content and attribute content.

The written words on a website or web app are typically what the W3C calls either heading content or phrasing content. Combined, they make up the vast majority of written content on the web.

Attribute content is content that comes from the value of things like aria-label, title, data-*, etc. Its purpose is for more niche development concerns.

Code sample featuting a button element and an iframe element. The button has a text label of 'save', and the iframe has a title attribute with a value set to 'YouTube: Crab rave'. Lines are drawn from the text label and iframe title to show the difference between text content and attribute content.

Both text content and attribute content have purpose and utility when creating accessible content for the web.

Why I think overuse of aria-label is a code smell

There are a few factors to be aware of. Let’s go over each:

1. aria-label’s varied support when declared on a non-interactive element

First off, aria-label is intended to only be used on interactive elements, and not non-interactive ones.

If you need a refresher on what HTML elements are interactive, they are:

The following declaration demonstrates how ARIA wants aria-label to be used:

<button aria-label="Save"></button>

While this declaration is an antipattern:

<div aria-label="create a copy"></div>

The Accessible Name and Description Computation algorithm—and the assistive technology that read the content generated by it—are looking for aria-label declared on an interactive element. Despite this, I commonly see aria-label used on non-interactive elements such as a div to attempt to:

When you declare aria-label on a non-interactive element, it is not being used as intended. Because of this, chances are good that it will:

The second bullet point is the one that breaks my heart.

Unnecessary control hints and descriptions almost is almost always a case of someone whose heart is in the right place, but is irresponsibly using ARIA without knowing both:

2. aria-label’s known support concerns when declared on an interactive element

Compared to a lot of its brethren, aria-label enjoys good support. However, this does not mean it has perfect support. Of note from the Accessibility Support page, aria-label:

An accessible name derived from aria-label that changes when its control is focused—which is required in order to activate the control—not announcing this state change is more concerning. This is because it is a common interaction pattern, to say nothing of other issues with the accessible name changing on an interactive element.

The support issue with Edge and Narrator gives me pause, in that they’re both Microsoft products and should theoretically be simpatico.

3. aria-label’s support concerns when used in a complicated interaction pattern

Grids, Tree Views, Treegrids, and bespoke custom components all are likely culprits. Be suspicious of any component—or series of interrelated components—that move away from things like simple boolean toggles and get into more complicated coordinated logic.

The reason I say this is that the more complicated your interactions become, the less reliable ARIA gets. The reason for this is as simple as it is depressing: ARIA is not a prioritized concern, and therefore has less thorough testing and support.

A practical example of this is a screen reader’s cursor “breaking” when reviewing focused text. Here, it will announce text that is not technically present. This means the onus is on the person using the screen reader to know:

  1. That this situation is occuring in the first place, and that
  2. They need to re-trigger reading the node to learn what content is actually present, if any.

Operating with this level of detail requires a good deal of technical literacy for the person using the screen reader, and is well-worth noting.

This point doesn’t even begin to cover things like regressions, and disdainful, willful ignorance. The only way to know with confidence if your complicated interaction pattern works? Test it with actual assistive technology. One way to make those tests easier? Remove aria-label.

4. aria-label may override the visible accessible name

Say you have a button with a visible text label of, “Snapshot” and an aria-label declaration of, “Save software image.

aria-label="Save software image"


To get right to it, this is a failure of WCAG Success Criterion 2.5.3: Label in Name. Someone using voice control software might think that they can say “Click snapshot” and have the button work. Unfortunately, the button’s aria-label declaration may override the visible text label.

Since aria-label is code, its value is invisible to anyone who does not have access to source code or developer inspector tools. This means the vast majority of the population have no way of knowing what the accessible name of the button actually is. This forces them to use annoying, repetitive, and esoteric workarounds to get what they need.

5. aria-label may be applied to something it is forbidden from being applied to

This is in addition to applying it to non-interactive elements.

For better or for worse, HTML is incredibly fault tolerant. You can declare anything on anything, regardless of if it is valid markup or not. Regardless, the browser will try its best to make sure any and all text content you put in those declarations shows up on screen.

Skip CodePen embed

ARIA is different. It has a strict grammar, and part of that is certain roles that are forbidden from being assigned an accessible name. The roles are:

So, that “helpful” hint placed on a del element?

<del aria-label="Sold out on October 20th, 2022">

It’s literally not allowed by the language’s own grammar.

6. Some browsers do not translate aria-label

Only Chrome and Edge support updating aria-label values for translation. The following browsers do not:

If someone requests a language other than the one the website or web app is written in, chances are good aria-label content will not update to match the new language.

In addition, content like aria-labels are oftentimes forgotten about as a consideration for translation services, as it is invisible code inserted by developers. This is especially relevant in organization where there is heavy siloing and barriers towards interdepartmental collaboration.

This means that both browsers and people may fail to provide a fully translated experience in situations where aria-label is utilized.

7. It is difficult to copy aria-label content or otherwise manipulated it as text

Text content is incredibly versatile. Right clicking on even a single word reveals a whole host of options for things we can do. You can copy it, cut it, define it, search the web for it, translate it, speak it aloud, link to it, print it, clip it, archive it, share it, etc.

This isn’t even counting the more esoteric things you can do like piping selected text into a terminal, creating a spoken phrase playlist entry from it, converting it into a QR code, and other horribly nerdy things I’m not aware of.

Using aria-label deprives someone of all that utility, unless they are incredibly technically literate. Of note, I’d like to stress the inability to be copied. Copying is a great feature, and is vital for things like researching, writing documentation, crafting how-to guides, or submitting a bug report.

8. aria-label content will not show up if styles fail to load

Aggressive firewalls, intermittent connection, shiesty service providers, bad caches, browser plugins, content blockers, non-standard browsers, large blocking assets, sloppy JavaScript, compromised ad networks, VPNs, CDN outages, scrapers and archivers, and panicky production hotfixes can all conspire to interrupt your style’s HTTP request.

Despite our industry’s various attempts to kill it, Progressive Enhancement is also still very much a thing. A text content label—visually hidden or otherwise—will always show the interactive control’s accessible name if styles fail to load.

A fake screenshot of a website where styles failed to load. There is a heading that reads, 'Your invoice'. Following are three unstyled buttons labeled, 'Save', 'Print', and 'Share'.
It’s ugly, but it works!

A Progressive Enhancement-friendly approach lets someone understand and take action on things, even under less-than-ideal conditions. An aria-label providing the accessible name cannot do this.

9. aria-label may override what aria-describedby is intended to announce

aria-describedby allows you to append the text of one element onto another element. This updates the target element’s announcement to be the combination of its initial accessible name and then text of the other element.

In testing, aria-label’s attribute content will override an element’s text content when creating the new aria-describedby-derived announcement.

Code sample featuting a button element and an anchor element. The button has a text value of 'share' and an aria-describedby attribute with a value of instructions. The anchor element has a text label of 'Terms and conditions', an aria-label attribute with a value of 'sharing rules', and an id attribute with a value of 'instructions'. Arrows point out how the button's text value is discarded in favor of the aria-describedby declaration. Following the code example is a label of 'Announcement', with a value called 'Share, sharing rules'.

This isn’t necessarily a bad thing per se, although I find it a confusing choice on which part of the overall declaration is honored. Given most developers’ level of familiarity with ARIA, I feel that removing as many potential surprises as possible is a good practice.

10. The First Rule of ARIA exists

The first rule of using ARIA states (emphasis theirs):

If you can use a native HTML element or attribute with the semantics and behavior you require already built in, instead of re-purposing an element and adding an ARIA role, state or property to make it accessible, then do so.

aria-label, like the rest of ARIA, has a time and place to be used. However, over-application and misapplication of it can do more harm than good.

Hopefully the previous ten points did their job in illustrating the reason this rule exists.

Well, what should I use instead of aria-label?

If it is important enough to need words, it is important enough to use text content.

Further reading