Search topics
aria-label on divs and spans that screen readers skip
A product list puts each star rating and its free-delivery icon in a span with an aria-label, but a plain span cannot be named, so many screen readers say nothing where the rating should be.
<span class="icon-truck" aria-label="Free delivery"> The problem
Screen reader users can't tell how a product is rated or whether delivery is free.
A shop's listing page shows two products as cards. Each card has the product name as a heading, a row of five stars with some of them filled, and, on one card, a small truck icon that means delivery is free. A sighted shopper sees at a glance that the apron is rated 4 out of 5 and ships for free, and the tote is rated 3. A screen reader in its reading mode reads "Linen apron, heading level 3", then "Canvas tote, heading level 3", and nothing in between. Another screen reader on the same page reads "Rated 4 out of 5, Free delivery". The developer tested with the second one, so the page looked finished.
The stars and the truck are drawn with CSS, so the elements that hold them are empty. To give them words, each one got an aria-label: <span class="stars" aria-label="Rated 4 out of 5"> and <span class="icon-truck" aria-label="Free delivery">. A span with no role has the role generic, and ARIA prohibits a name on that role. Browsers and screen readers are free to drop the label, and several common ones do in reading mode, because there is nothing to name: it is not a control, an image or a region. So the rating and the offer have no text alternative that reliably reaches assistive technology, which fails 1.1.1. axe reports the attribute itself under 4.1.2, because the name it was meant to provide is not exposed.
The failure depends on the screen reader and how it is used: one may read the label in one mode and skip it in another. That is what makes it easy to miss. A page that works only with the screen reader the developer happens to use doesn't work.
2 of 10 groups affected
- No vision (affected)
- Low vision (not affected)
- Colour vision (not affected)
- No hearing (not affected)
- Hard of hearing (not affected)
- No speech (not affected)
- Motor (not affected)
- Reach, strength (not affected)
- Cognitive (affected)
- Seizures (not affected)
- Without vision Screen reader users hear each product's name and nothing else, so they can't compare ratings or find the items that ship for free. Some screen readers do read the label, so two people on the same page hear different things.
- Limited language, cognitive and learning abilities People who don't know that a small truck means free delivery have only the icon to go on while the offer is never written in words.
axe's aria-prohibited-attr flags an aria-label on a div or span with no role, so the attribute is found. Many teams ignore the result, because when they check with one screen reader, the label is read. The scanner can't tell whether the words matter: an aria-label repeating visible text is harmless clutter, while one carrying a rating is lost information. A quick AI fix often adds role="img" to any element with a label, including ones that contain text or links, which then disappear inside the image. Another often moves the label into a title attribute, which most screen readers don't read either. Whether each label carries something people need, and whether it now reaches them, is for a person to check.
Try it
Compare the cards with what a screen reader reads.
The panel shows what a screen reader that skips a label on a span reads. Can you tell how each product is rated, and which one ships for free?
Broken Can you fix this?
Linen apron
Canvas tote
A screen reader that skips the label says:
- list, 2 items
- Linen apron, heading level 3
- Canvas tote, heading level 3
A recreated demo. The broken version is intentionally inaccessible.
The fix
Put each label where it can be read: on an element that can be named, or in the content as text.
- 1Use aria-label only on elements whose role can be named: controls, landmarks, images and frames.
- 2Give a graphic role="img" with its label, or put its meaning in the content as text.
- 3Hide the graphic itself with aria-hidden="true" when its meaning is written beside it.
<ul class="products"> <li class="card"> <h3>Linen apron</h3> <span class="stars stars-4" aria-label="Rated 4 out of 5"></span> <span class="icon-truck" aria-label="Free delivery"></span> </li> <li class="card"> <h3>Canvas tote</h3> <span class="stars stars-3" aria-label="Rated 3 out of 5"></span> </li></ul>Give each graphic a role that can be named
<ul class="products"> <li class="card"> <h3>Linen apron</h3> <span class="stars stars-4" role="img" aria-label="Rated 4 out of 5"></span> <span class="icon-truck" role="img" aria-label="Free delivery"></span> </li> <li class="card"> <h3>Canvas tote</h3> <span class="stars stars-3" role="img" aria-label="Rated 3 out of 5"></span> </li></ul>The stars and the truck are background images drawn by CSS, so each span is empty.
The spans are empty and have no role, so their labels may never be read, and the rating and the delivery offer are lost for many screen reader users.
role="img" makes each span one image, and an image can be named, so screen readers read "Rated 4 out of 5, image" and "Free delivery, image". It is the smallest change: one attribute on each element. It works only because the spans hold nothing but a picture. role="img" hides everything inside it, so never put it on an element that contains text or a link. The words still exist only for assistive technology, and the truck still means nothing to a sighted shopper who doesn't know it.
All three put the rating and the delivery offer where screen readers read them, so all three meet 1.1.1. role="img" is the smallest change, but its words reach only assistive technology, and some page translators skip aria-label. Hidden text reads in every screen reader and translates, but it too leaves sighted people with only the icon. Writing the words on the card is plain HTML, reaches everyone who reads the page, and leaves nothing for ARIA to carry. Use it. Keep the other two for a place with truly no room for words, and then prefer hidden text, which needs no role and is translated.
Which elements can take a name. aria-label works on elements with a role that can be named: controls such as buttons, links and fields, landmarks such as nav, images, iframes, dialogs, tables and groups. ARIA prohibits a name on these roles, among others.
generic, the role of adivorspanwith no other role;paragraph, the role ofp;presentationandnone, which remove an element's role on purpose;- text-level roles such as
strong,emphasis,code,time,mark,deletion,insertion,subscript,superscript,termanddefinition, andcaption.
aria-labelledby is prohibited on the same roles, so pointing it at hidden text doesn't help.
Text that should be read differently. A label is often added to make text read differently, for example <time aria-label="3 October 2026">3 Oct</time> or <span aria-label="12 items">12</span>. Neither element can be named. Write the fuller words as visible text, or add visually hidden text beside the short form and hide the short form with aria-hidden="true". Screen readers already read short dates and numbers well, so check that the change is needed before making it.
title and aria-describedby are not workarounds. A title attribute is read by some screen readers on some elements and shown only on mouse hover. aria-describedby is allowed on any element, but screen readers read descriptions mainly when a control takes focus, so a description on a span is easy to miss.
role="img" hides its content. Everything inside an element with role="img" is treated as part of the picture and not read. Use it only on an element that holds a graphic and nothing else, and put any count or link outside it.
Component props. Many components take a label prop and pass it to their outer div as aria-label. Check what a component renders: a label on a wrapper with no role is lost. In a shared icon or rating component, set role="img" whenever a label is passed, or render the label as hidden text.
Fixing this with an AI coding assistant? Get this guide as Markdown
Verify the fix
4 checks, no mouse.
Test with: Screen reader
Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat whenever a shared component that renders labels changes.
Limits
This is a starting pattern, not a guarantee for every application. Class names are placeholders, and the stars and truck stand for any graphic drawn with CSS or an icon font. Screen readers change how they treat prohibited names between versions, and some read them in one mode but not another, so a pass with one screen reader proves little. Whether a label carries something people need, or only repeats visible text, depends on the page and needs a person to judge. Test the complete journey with your target assistive technology.
This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.
References
- developer.mozilla.org: aria-label (opens in a new tab)
- w3.org: wai-aria-1.2 (opens in a new tab)
- w3.org: using-aria (opens in a new tab)
- WCAG Understanding: non-text-content (opens in a new tab)
- WCAG Understanding: name-role-value (opens in a new tab)
- dequeuniversity.com: aria-prohibited-attr (opens in a new tab)
Keep this fixed as your code changes.
Turn guides like this into rules your coding agents and CI follow, then have the result retested with a keyboard and screen readers.