The difficulty of finding out how to use WAI-ARIA correctly


Even with the best of intentions and diligent searching it is still rather difficult to work out how to correctly use WAI-ARIA – even for rather simple use cases.

Author: Detlev Fischer (@wcagtest)

Imagine you are a developer trying to work out how to implement a simple dynamic element, actually something very simple – you have some sort of heading and when you activate it, it should expand to reveal a previously hidden section underneath.

You know that WAI-ARIA can be used to improve the accessibility of dynamic content for screen reader users. You have worked out how to use display:none and aria-hidden to hide visually hidden content also for screen reader users – you actually just set aria-hidden and use a CSS rule to control the visibility of the expandable section, and the aria-pressed state (and in turn, visual appearance) of the button (see a crude example).

You also realize that you should use role="button" on the link that displays the expandable section since the link doesn’t lead anywhere - it just changes the visible state of the section underneath. So the keyboard focus will remain on the link with role="button", and repeated activation will just toggle the visibility of the expandable section. Fine so far.

Reading further into WAI-ARIA, you realize that there is the aria-expanded state that could (or should) also be applied – you just wonder whether it would be an attribute set on your triggering link with role="button", or an attribute set on the expandable section.

Searching for aria-expanded

Delving into WAI-ARIA documentation, you decide to search for aria-expanded and see what comes up (that’s how I often approach such questions, but there may be sounder ways). Doing just that, you will get (at my time and place, and searching with Google) in top position a link to an article on aria-expanded by Marco Zehe, then some Microsoft documentation, and in third place, some pretty authoritative looking documentation: ARIA States and Properties - W3C. In the section on aria-expanded, you find the following definition:

aria-expanded (state)

Indicates whether the element, or another grouping element it controls, is currently expanded or collapsed.

Which does not answer your question whether you should set the state on the button or on the expandable section, or on both – the or seems to indicate it could be either/or (but not both).

You also find the recommendation:

If the element with the aria-expanded attribute controls the expansion of another grouping container that is not 'owned by' the element, the author SHOULD reference the container by using the aria-controls attribute.

In other words, the trigger button should in any case carry the aria-expanded attribute and in addition, the aria-controls attribute pointing to the id of the expandable section.

You return to your search engine to find some implementation examples or more concrete advice. Next there is a Paul J. Adams blog entry on aria-expanded. Paul describes implementation problems of various ways of indicating the expanded state (including, besides aria-expanded, the alt and title attributes and hidden text) and reporting some not so encouraging screen reader testing results.

You skip this for now to look for some more authoritative information. Wasn’t there an ARIA Best Practices document that should do the trick? You do a search on aria-expanded best practice which brings up in fourth position a fairly authoritative-looking document: Using WAI-ARIA in HTML - W3C. Looking here, you find that aria-expanded can be used on many (most) aria-roles, including button - even including the roles alert and dialog.

Searching for aria-expanded on the page you find a section on the summary element being used in a scripted polyfill with a recommendation to use role="button" with aria-expanded="true", but this is not your use case, so you look further.

Next, you look at a page from the OpenAjax Initiative on using aria-controls and aria-expanded which is oddly named Example 22 - Hide/Show: Region is exclusive. This page has the benefit of a working implementation looking like a crude tab panel (also supporting keyboard interaction) and code examples.

Here you find the aria-expanded attribute set on the controlling li with role="button", not on the section that is hidden and revealed. It is interesting to note that while the example looks very much like a tab panel, the tablist, tab and tabpanel roles are nowhere to be seen. Maybe look somewhere else?

Going back to Marco Zehe’s now slightly dated blog entryfrom 2010 that still comes up top in Google when searching just for aria-expanded, you follow through to the example he pointed to. In this implementation, aria-expanded is used both on the triggering heading (made keyboard-navigable via tabindex and role="button") and on the ul that wraps round the expandable section. In addition, aria-controls is set on the trigger and points to the controlled section (the ul).

Finally, you recall another authoritative source, the WAI ARIA 1.0 Authoring Practices. The document seems to have been updated recently, it says "W3C Working Draft 7 March 2013".

Searching for aria-expanded here leads straight into a longish explanation of how to implement keyboard navigation in an accordion widget – fair enough, since the use case you have in mind can be considered the simplest form of accordion (with just one expandable section). The text describes the proper keyboard handling when tabbing and arrowing through an accordion and has the following snippet:

When the corresponding panel is expanded (its aria-expanded state is 'true'), then focus moves to the first focusable element in the panel.

From this, it seems clear that the expandable section (the accordion panel) itself should carry the aria-expanded attribute. Further down, we are advised that "An accordion should manage the expanded/collapsed state of each tab by maintain[ing] its aria-expanded state." Which again sounds as if this state should be applied to the tab, i.e., to the accordion header as well. If this is what should be done (apply aria-expanded both to the trigger (tab, button) and to the section (here: tabpanel) that is expandable, why is this nowhere clearly stated?

Since you want to double-check, you follow the example provided, the Open Ajax Alliance Accordion, which, besides being spelled accordian, does not set the aria-expanded state on the expandable sections (they have role="tabpanel" here). So we are back to square one. Maybe there are other examples to confirm this is what we need to do?

Further searching for "accordion widget" brings up a jQuery example, the jQuery Accordion. jQuery is a popular JavaScript framework and the team has made an effort to use WAI-ARIA, so this should be interesting to look at. Looking at the source code, you find that here, the aria-expanded attribute is applied only to the expandable divs with role="tabpanel", not to the triggering headings (h3 with role="tab") – these just reference the expandable divs via the aria-controls property.


If I were a developer wanting to use WAI-ARIA on a simple expandable bit of content, I would be rather exasperated by now. Given the inconsistent and confusing state of ARIA documentation and best practice examples available, is it surprising that we see little use of ARIA and many cases where it is used incorrectly?

Last line: Having done all this searching, I am still not sure what would be the proper way of implementing my use case. I would lean to using aria-expanded only on the element that actually expands and contracts, not on the trigger (as in the jQuery example) – but currently, I have little to back up that preference.