A subset of ARIA roles are defined as having presentational children. These are mostly a subset of widget roles
The DOM descendants are presentational. User agents SHOULD NOT expose descendants of this element through the platform accessibility API. If user agents do not hide the descendant nodes, some information may be read twice.
What does it mean
Whatever the child elements of these roles represent semantically should not be exposed in the user agent accessibility tree and therefore not conveyed to users.
The SHOULD NOT in this case is a normative keyword:
4. SHOULD NOT This phrase, or the phrase “NOT RECOMMENDED” mean that there may exist valid reasons in particular circumstances when the particular behavior is acceptable or even useful, but the full implications should be understood and the case carefully weighed before implementing any behavior described with this label.
source: Key words for use in RFCs to Indicate Requirement Levels
What flows from this?
Example of a
<button> element (
role="button") with a heading
<button> <h2>Heading</h2> </button>
note: this code is non conforming and must not be used.
Should (as per the ARIA spec) be exposed like this:
<button> Heading </button>
The chrome and Firefox browsers ignore this should not and expose the heading
JAWS respects the should not and does not announce the heading semantics
(and does not recognise the heading when the H key is pressed):
NVDA ignores the ARIA spec, respects the accessibility tree (sort of) and announces the heading semantics
(yet does not recognise the heading when the H key is pressed):
What about VoiceOver?
Meanwhile, Safari on Mac OS and all browsers on iOS (as they are all WebKit under the hood) respect the ARIA should not expose the semantics of child elements of those roles that are defined as child presentational: true. Thus VoiceOver never conveys the semantics of such children.
There is a problem
Browsers and assistive technology are free to decide in the should not case what the best outcome is for their users and implement accordingly. Unfortunately this has led to an interoperability nightmare where some users get the information under some circumstances, and others don’t, and where screen readers convey information that is sometimes confusing. What is needed is some collaboration and agreement between implementers on how such cases should handled.
To be perfectly clear
The inclusion of controls or structured content as descendants of roles with child presentational: true is an author conformance error. What is allowed:
Phrasing content, but with no interactive content descendants, and no descendants with a
In an ideal web, UI developers would know how to use HTML+ARIA correctly, and browser and screen reader implementers would not have to decide what they need to do with substandard code patterns. But developers don’t always code as they should…
For each screen reader test the following:
- Navigate by tabbing (results reported in table below)
- Navigate by virtual cursor arrow keys
- Navigate by virtual cursor element navigation
- Display element list dialog where relevant
|tests||Children in Chrome acc tree
(Windows and Mac OS)
|Children in Firefox acc tree (Windows)||
Children in Safari acc tree Mac OS)
|focusable children announced in JAWS (tab key navigation, Chrome)||focusable children announced in NVDA (tab key navigation, Firefox)||children announced in VoiceOver Mac OS||children announced in VoiceOver iOS|
|3. role=checkbox (no tabindex)||yes||yes||no||
||HTMLZ link graphic||no||list exposed, link text but not link|
|6. role=img with aria-label||yes||yes||no||
||HTMLZ link graphic poot||no||list exposed, link text but not link|
|7. role=menuitemcheckbox with aria-label||yes||yes||no||link announced as poot, not checked||
|8. role=menuitemcheckbox with aria-label||yes||yes||no||HTMLZ link||
|9. role=meter with aria-label||yes||yes||no||link focused no announcement||
|10. role=menuitemradio with aria-label||yes||yes||no||HTMLZ link||
|11. role=option with aria-label||yes||yes||no||HTMLZ link||HTMLZ link||no||no|
|12. role=progressbar with aria-label||yes||yes||no||link focused no announcement||HTMLZ link||no||no|
|13. role=radio with aria-label||yes||yes||no||link announced as poot radio button not checked, 1 of 1||HTMLZ link||no||no|
|14. role=scrollbar with aria-label||yes||yes||no||link focused no announcement||HTMLZ link||no||no|
|15. role=separator with aria-label||yes||yes||no||link focused no announcement||HTMLZ link poot separator||no||no|
|16. role=slider with aria-label||yes||yes||no||link focused no announcement||HTMLZ link||no||no|
|17. role=switch with aria-label||yes||yes||no||link announced as poot, toggle button pressed||HTMLZ link||no||no|
|18. role=tab with aria-label||yes||yes||no||link announced as poot, tab||HTMLZ link||no||no|
Thanks to @Lloydi for the Mac OS testing!