The main reason to use an accordion is to organize a potentially long series of info, in order to show them only when the user explicitly declares, with a mouse click, the desire to learn more about a single topic, a perfect example of progressive disclosure as an interaction design pattern.
This already helps us make a couple of decisions: user action is to visualize content (action, not navigation: this means we will use a <button>
for this, you can see the difference in this article), this content shouldn't be availble at first to simplify page content fruition (truly hidden, so we will use a display: none
CSS style or a hidden="true"
attribute declaration, instead of visually-hidden
utility class, so neither a screen reader will be able to reach the content).
The action should be explicit, since we want to cut away content: so the action will be triggered by a click/enter/spacebar, not with a simple focus. Great, no additional JS required for this.
We want, also, to be able to exit the content, to compress the area: the obvious way is to add an event listener to ESC keypress when inside the panel or when interacting with the toggler via keyboard.
Enough talk, let's do it.
We have essentially a list. The best option is probably a "definition list" <dl>
, that requires couples of <dt>
description term and <dd>
description detail.
<dl>
<dt>Toggler1</dt>
<dd>Details1</dd>
<dt>Toggler2</dt>
<dd>Details2</dd>
</dl>
To trigger the panel visualization we will interact with each element's "description term", which basically contains, in its simplest form, a short text and a caret to visually suggest the presence of more content (it can be a downward caret, or a plus, for example, that can transform in a minus or an upward arrow);
In order to properly use aria-controls
attribute (if you want to use it) unique ids are needed for both togglers and panels. Using a naming convention could help in programmatically attributing the right names without hassle. I've gone with {accordionId}-toggler(or panel)-{index}
.
<dl id="myAccordion" class="accordion">
<dt class="accordion-item">
<button
type="button"
id="myAccordion-toggler-0"
aria-expanded="false"
class="accordion-item__toggler"
aria-controls="myAccordion-panel-0">
Toggler1
<span class="accordion-item__caret">
</button>
</dt>
<dd id="myAccordion-panel-0">Details1</dd>
The support for aria-controls
is very limited and many developers decide not to use it (opens in a new tab) (only JAWS supports it, to this date). But since the accordion panel immediately follows the accordion toggler, in this pattern, this is not a big issue as screen reader users will move to the panel as they continue down the page.
Another useful aria attribute, here, is aria-expanded="false"
, that will become "true
" when the panel will be open.
Using aria-expanded
only is now possible to set everything up, from CSS styles to the JS dynamics needed to toggle open/close states. The following example should be quite straightforward. If not, let me know and I'll update this article.