Web Accessibility Training
Toronto, Montreal, Ottawa and worldwide via Webex More

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 and aria-describedby are robustly supported for interactive content elements such as links and form controls including the many input types.
  • OK on the <nav>, and <main> elements but not on <footer>, <section>, <article>, <aside> or <header>.
  • OK on div elements with role=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 role=banner, complementary, contentinfo. ignored on all static content except above

aria-describedby well supported on static content

Aria-label, aria-labelledby are override headings.

landmark roles OK.

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
  • Talkback on Android overrides the content of all landmarks with aria-label or aria-labelledby.
  • JAWS doesn't support them on 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.
  • Don't use aria-label or aria-labelledby on any heading elements because it overrides them on NVDA, VoiceOver and Talkback. JAWS ignores them.
  • Don't use aria-label or aria-labelledby on any other non-interactive content such as p, legend, li, or ul, because it is ignored.
  • Don't use 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 or aria-labelledby on all static content except for nav, main, table, th, and td, which it reads correctly (all browsers). It gives user option to hear aria-describedby on all static content.
    • NVDA on WIN10 overrides heading text with aria-label or aria-labelledby, ignores it on other static content except for nav which it reads correctly (all browsers). It ignores aria-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 correctly aria-label, aria-labelledby, aria-describedby on the table element but not th or td
    • VoiceOver on MacOS overrides heading text with aria-label or aria-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 read aria-label , aria-labelledby and aria-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 read aria-describedby if its on an interactive element, an image role, or a landmark role. Poor support of aria-label , aria-labelledby and aria-describedby on table, th or td
    • Talkback on Android. All static text is overridden by aria-label or aria-labelledby except for ul and li which are ignored, nav and table works correctly. It always reads aria-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 the aria-label or aria-labelledby overrides the text
    • If its a heading role , aria-label or aria-labelledby overrides it on NVDA, VoiceOver and Talkback. JAWS ignores the aria-label or aria-labelledby.
    • If the role is a static role then aria-label will be ignored by all screen readers except Talkback which overrides the static content (except if its listitem 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"
    div element with aria-label="test 10"
    legend element with aria-label="Test 11". It is inside a fieldset element
    • 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"
    footer element with aria-label="test 20"

    header element with aria-label="test 20"

    main element with aria-label="test 20"

    section element with aria-label="test 20

    This is a table with aria-label="test21" on table elementheader 2
    td 1td 2
    This is a table with aria-label="test22" on th elementheader 2
    td 1td 2
    This is a table with aria-label="test23" on td elementheader 2
    td 1td 2
    === aria-labelledby ===

    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"
    div element with aria-labelledby="static-text
    legend element with aria-labelledby="static-text" . It is inside a fieldset element
    • 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"
    footer element with aria-labelledby="static-text"

    header element with aria-labelledby="static-text"
    main element with aria-labelledby="static-text"


    section element with aria-labelledby="static-text"

    This is a table with aria-labelledby="static-text" on table elementheader 2
    td 1td 2
    This is a table with aria-labelledby="static-text" on th elementheader 2
    td 1td 2
    This is a table with aria-labelledby="static-text" on td elementheader 2
    td 1td 2
    === aria-describedby ===

    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"
    div element with aria-describedby="static-text"
    legend element with aria-describedby="static-text" . It is inside a fieldset element
    • 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"
    footer element with aria-describedby="static-text"

    header element with aria-describedby="static-text"
    main element with aria-describedby="static-text"

    section element with aria-labelledby="static-text"

    This is a table with aria-describedby="static-text" on table elementheader 2
    td 1td 2
    This is a table with aria-describedby="static-text" on th elementheader 2
    td 1td 2
    This is a table with aria-describedby="static-text" on td elementheader 2
    td 1td 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 13" with role=img
    span element with aria-label="test 14" with role=button and tabindex=0

    div element with aria-label="test 15" with role=button and tabindex=0

    span element with aria-label="test 16" with role=heading and aria-level=1
    div element with aria-label="test 17" with role=heading and aria-level=1
    span element with aria-label="test 18" with role=link
    span element with aria-label="test 19" with role=listitem
    div element with aria-label="test 20" with role=banner
    div element with aria-label="test 20" with role=contentinfo
    div element with aria-label="test 20" with role=complementary
    div element with aria-label="test 20" with role=main
    div element with aria-label="test 20" with role=search
    div element with aria-label="test 20" with role=banner
    div element with aria-label="test 20" with role=navigation

    === aria-labelledby ===

    span element with aria-labelledby="static-text" with role=img
    div element with aria-labelledby="static-text" with role=img
    span element with aria-labelledby="static-text" with role=button and tabindex=0

    div element with aria-labelledby="static-text" with role=button and tabindex=0

    span element with aria-labelledby="static-text" with role=heading and aria-level=1
    div element with aria-labelledby="static-text" with role=heading and aria-level=1
    span element with aria-labelledby="static-text" with role=link
    span element with aria-labelledby="static-text" with role=listitem
    div element with aria-labelledby="static-text" with role=banner
    div element with aria-labelledby="static-text" with role=contentinfo
    div element with aria-labelledby="static-text" with role=complementary
    div element with aria-labelledby="static-text" with role=main
    div element with aria-labelledby="static-text" with role=search
    div element with aria-labelledby="static-text" with role=banner
    div element with aria-labelledby="static-text" with role=navigation

    === aria-describedby ===
    span element with aria-describedby="static-text" with role=img
    div element with aria-describedby="static-text" with role=img
    span element with aria-describedby="static-text" with role=button and tabindex=0

    div element with aria-describedby="static-text" with role=button and tabindex=0

    span element with aria-describedby="static-text" with role=heading and aria-level=1
    div element with aria-describedby="static-text" with role=heading and aria-level=1
    span element with aria-describedby="static-text" with role=link
    span element with aria-describedby="static-text" with role=listitem
    div element with aria-describedby="static-text" with role=banner
    div element with aria-describedby="static-text" with role=contentinfo
    div element with aria-describedby="static-text" with role=complementary
    div element with aria-describedby="static-text" with role=main
    div element with aria-describedby="static-text" with role=search
    div element with aria-describedby="static-text" with role=banner
    div element with aria-describedby="static-text" with role=navigation
    Here is some text to reference with id="static-text"

    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, img role, or nav, main, search roles.

    aria-describedby well supported on static content

    Aria-label, aria-labelledby are override headings.

    landmark roles OK.

    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, img role, or nav, main, search roles.

    aria-describedby well supported on static content

    Aria-label, aria-labelledby are override headings.

    landmark roles OK.

    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, img role, or nav, main, search roles.

    aria-describedby well supported on static content

    Aria-label, aria-labelledby are override headings.

    landmark roles OK.

    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-label when virtual cursor hits the elements and then let user enter and read the contents.

    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-label when virtual cursor hits the elements and then let user enter and read the contents.

    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. nav reads the label and then the contents correctly. On other elements there is no aria-label read.

    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


    EMAIL

    help at can hyphen adapt dot com, (spoken phonetically to trick spam bots)

    PHONE

    six one three, eight zero six, nine zero zero five

    SOCIAL MEDIA