What happens with aria-labelledby, aria-label and aria-describedby
on static HTML elements?
An interesting Twitter thread sprung up from a Tweet about overriding elements with aria-label
with a follow up thread with my colleagues.
I was asked to test the results of aria-label
on static content. This is what is below, along with aria-labelledby
and aria-describedby
, as well as an examination of what is "supposed to happen" according to the Accessible Name computation.
Conclusion: TLDR
Screen readers are all over the map on what they do when an aria-label
, aria-labelledby
, and aria-describedby
on static content
Results
-
aria-label
,aria-labelledby
andaria-describedby
are robustly supported for interactive content elements such as links and form controls including the manyinput
types. - OK on the
<nav>
, and<main>
elements but not on<footer>
,<section>
,<article>
,<aside>
or<header>
. - OK on
div
elements withrole=navigation
,role=search
,role=main
,role=img
. - OK on
table
element.
Quirks and Quarks when aria-label
, aria-labelledby
and aria-describedby
are static content
JAWS | NVDA | VoiceOver | Talkback | |
---|---|---|---|---|
aria-labelledby and aria-label |
Ignored on aria-describedby well supported on static content |
Aria-label, aria-labelledby are override headings. On other elements there is no aria-label aria-labelledby read. |
All the headings are overridden. User then has to interact with heading to hear it, but would not discover that easily. Other elements read aria-label when virtual cursor hits the elements and then let user enter and read the contents. |
All the static content is overridden except list items which are ignored (weird) |
aria-describedby | Well supported on static content |
Not supported on static content |
Not supported on static content | Well supported on static content |
aria-label
or aria-labelledby.
role=banner
, role=complementary
, role=contentinfo.
NVDA, VoiceOver, and Talkback are OK
aria-label
, aria-labelledby
and aria-describedby
work well on table
, th
and td
elements with a few exceptions for NVDA, VoiceOver on iOS, and Talkback discussed in next section. aria-label
or aria-labelledby
on any heading elements because it overrides them on NVDA, VoiceOver and Talkback. JAWS ignores them. aria-label
or aria-labelledby
on any other non-interactive content such as p
, legend
, li
, or ul
, because it is ignored. aria-label
or aria-labelledby
on a span
or div
unless its given a role
. When aria-label
or aria-labelledby
are on interactive roles (such as a link
or button
) or an img
role, they override the contents of the div
or span
. Other roles besides Landmarks (discussed above) are ignored.aria-describedby
on a span
or div
will be ignored by NVDA and VoiceOver unless given an interactive role
, an image or landmark role
. JAWS and Talkback are OK. aria-describedby
will be ignored by NVDA and VoiceOver on any other static content. JAWS and Talkback are OK.All of the above also work the same in iframes. Both aria-label
and aria-labelledby
have the same behaviour with screen readers and the Accessibility API, but aria-label
should be reserved for when there is no visible text on the page to reference or when keeping track of id
values would be too difficult.
I filed an issue with the ARIA spec to define the term "user interface elements" (do they include static elements?) with the hope that it will help screen readers manufacturers know what to do.
Short summary of findings
- JAWS on WIN10 ignores
aria-label
oraria-labelledby
on all static content except fornav
,main
,table
,th
, andtd
, which it reads correctly (all browsers). It gives user option to heararia-describedby
on all static content. - NVDA on WIN10 overrides heading text with
aria-label
oraria-labelledby
, ignores it on other static content except fornav
which it reads correctly (all browsers). It ignoresaria-describedby
on all static content unless it's been given an interactive role and tabindex=0, and then only if the users tabs to it (not arrows to it). It reads correctlyaria-label
,aria-labelledby
,aria-describedby
on thetable
element but notth
ortd
- VoiceOver on MacOS overrides heading text with
aria-label
oraria-labelledby
, user can interact with heading to dig in and get the text, but most users would miss that because its not announced when read. Other elements readaria-label
,aria-labelledby
andaria-describedby
when virtual cursor hits the elements and then let user enter and read the contents. (Chrome and Safari) - VoiceOver on iOS. All the headings are overridden by the VO, ignores it on other static content except for
nav
which it reads correctly (Safari), It only readaria-describedby
if its on an interactive element, an image role, or a landmark role. Poor support ofaria-label
,aria-labelledby
andaria-describedby
ontable
,th
ortd
- Talkback on Android. All static text is overridden by
aria-label
oraria-labelledby
except forul
andli
which are ignored,nav
andtable
works correctly. It always readsaria-describedby
on all static content.
What happens when roles are added?
- If the role is an interactive
role
(that can be clicked on such as button or link) or an image role thearia-label
oraria-labelledby
overrides the text - If its a heading
role
,aria-label
oraria-labelledby
overrides it on NVDA, VoiceOver and Talkback. JAWS ignores thearia-label
oraria-labelledby
. - If the
role
is a static role thenaria-label
will be ignored by all screen readers except Talkback which overrides the static content (except if itslistitem
role, then its ignored).
Static text Element tests with aria-roles with aria-label, aria-labelledby and aria describedby
BEGIN TEST ==========This is an h1 with aria-label="test 1"
h2 with aria-label="test 2"
h3 with aria-label="test 3"
h4 with aria-label="test 4"
h5 with aria-label="test 5"
h6 with aria-label="test 6"
p element with aria-label="test 8"
span element with aria-label="test 9"- li with aria-label="Test 11". It is inside of a ul element
- li element inside ul element. The ul has aria-label="Test 11"
This is a table with aria-label="test21" on table element | header 2 |
---|---|
td 1 | td 2 |
This is a table with aria-label="test22" on th element | header 2 |
---|---|
td 1 | td 2 |
This is a table with aria-label="test23" on td element | header 2 |
---|---|
td 1 | td 2 |
aria-labelledby="static-text" on an h1 referencing text below
h2 with aria-labelledby="static-text"
h3 with aria-labelledby="static-text"
h4 with aria-labelledby="static-text"
h5 with aria-labelledby="static-text"
h6 with aria-labelledby="static-text"
p element with aria-labelledby="static-text"
span element with aria-labelledby="static-text"- li with aria-labelledby="static-text" . It is inside of a ul element
- li element inside ul element. The ul has aria-labelledby="static-text"
This is a table with aria-labelledby="static-text" on table element | header 2 |
---|---|
td 1 | td 2 |
This is a table with aria-labelledby="static-text" on th element | header 2 |
---|---|
td 1 | td 2 |
This is a table with aria-labelledby="static-text" on td element | header 2 |
---|---|
td 1 | td 2 |
aria-describedby="static-text" on an h1 referencing text below
h2 with aria-describedby="static-text"
h3 with aria-describedby="static-text"
h4 with aria-describedby="static-text"
h5 with aria-describedby="static-text"
h6 with aria-describedby="static-text"
p element with aria-describedby="static-text"
span element with aria-describedby="static-text"- li with aria-describedby="static-text" . It is inside of a ul element
- li element inside ul element. The ul has aria-describedby="static-text"
This is a table with aria-describedby="static-text" on table element | header 2 |
---|---|
td 1 | td 2 |
This is a table with aria-describedby="static-text" on th element | header 2 |
---|---|
td 1 | td 2 |
This is a table with aria-describedby="static-text" on td element | header 2 |
---|---|
td 1 | td 2 |
End TEST ==========
Static text Elements with role attributes and aria-label, aria-labelledby and aria describedby
BEGIN TEST ======= aria-label ===
span element with aria-label="test 12" with role=img
div element with aria-label="test 15" with role=button and tabindex=0
span element with aria-label="test 19" with role=listitem
=== aria-labelledby ===
span element with aria-labelledby="static-text" with role=img
div element with aria-labelledby="static-text" with role=button and tabindex=0
span element with aria-labelledby="static-text" with role=listitem
=== aria-describedby ===
span element with aria-describedby="static-text" with role=img
div element with aria-describedby="static-text" with role=button and tabindex=0
span element with aria-describedby="static-text" with role=listitem
END OF TEST ===============
Results
JAWS | NVDA | VoiceOver | Talkback | |
---|---|---|---|---|
FireFox (Sept 2018) | Aria-label, aria-labelledby are ignored on all static content unless it has a an interactive role, aria-describedby well supported on static content |
Aria-label, aria-labelledby are override headings. On other elements there is no aria-label aria-labelledby read. aria-describedby pooly supported on static content |
N/A | N/A |
IE (Sept 2018) | Aria-label, aria-labelledby are ignored on all static content unless it has a an interactive role, aria-describedby well supported on static content |
Aria-label, aria-labelledby are override headings. On other elements there is no aria-label aria-labelledby read. aria-describedby pooly supported on static content |
N/A | N/A |
Chrome (Sept 2018) | Aria-label, aria-labelledby are ignored on all static content unless it has a an interactive role, aria-describedby well supported on static content |
Aria-label, aria-labelledby are override headings. On other elements there is no aria-label aria-labelledby read. aria-describedby pooly supported on static content |
All the headings are overridden by the VO, User then has to interact with heading to hear it, but would not discover that easily. Other elements read aria-describedby pooly supported on static content |
All the static content is overridden except list items which are ignored (weird) aria-describedby well supported on static content |
Safari MacOS (Sept 2018) |
N/A | N/A | All the headings are overridden by the VO, User then has to interact with heading to hear it, but would not discover that easily. Other elements read aria-describedby pooly supported on static content |
N/A |
Safari iOS (Sept 2018) |
N/A | N/A | All the headings are overridden by the VO. aria-describedby pooly supported on static content |
N/A |
What do the Specs
say?
The Accessible Name Computation spec provides a calculation of an accessible NAME. the definition says:
The accessible name is the name of a user interface element. ...So I tried to look up "user interface element", in the glossary. There is no definition for "user interface element" in the ARIA spec, the Accessible Name Computation spec or the HTML 5.2. It is clear that different assistive technologies are interpreting this differently. JAWS doesn't override headings with
aria-label
, which NVDA, VO on iOS and MacOS do. So these AT's are interpretting a heading as a user interface element. I filed a bug with the aria spec and will update this article with the results of that discussion.
Feel free to comment on Twitter @davidmacd
Author information:
David MacDonald is a veteran WCAG member, co-editor of Using WAI ARIA in HTML5 and HTML5 Accessibility Task Force Member. Opinions are my own.
CONTACT US
For a quote or just to chat about your organization's needs
PHONE