Basically, I wanted to consider accessibility or “A11y” when building this component.
I broke down my research approach this way:
- CRAFT A DEFINITION: What is the purpose and function of the accordion component for users and developers? How did the accordion create pain-points for users relying on assistive technology? What accessibility concerns did developers overlook when coding an accordion component?
- BUILD OUT SEMANTIC HTML STRUCTURE: Good accessibility requires reliance on semantic HTML whenever possible. What would that look like?
- INCORPORATE WAI-ARIA: I figured that I would need to incorporate WAI-ARIA attributes to make my accordion accessible to the screen readers, but I wasn’t sure what I’d need to add. Knowing that the basic rule of WAI-ARIA is to not use it if you don’t have to, I wanted to build an accordion that relied on it in a very concise manner.
- TEST WITH VOICEOVER: I wanted to be sure that I tested whatever examples I built with Voiceover, which is my screenreader of choice since it is free and already installed on my Mac.
What is an accordion component?
The accordion component organizes multiple sections of content in a vertical manner. Each section has an accordion header, which is a title or question that refers to the content embedded in the corresponding accordion panel. The accordion gives the user the freedom to decide which sections of content they want to show or hide.
From a UX perspective, the accordion’s functionality is particularly useful because it solves the problem of limited screen real estate by allowing for more content to be embedded in small areas. For example, accordions are often selected as the design pattern for FAQ sections. Imagine the frustration of a mobile user having to endlessly scroll or a screen reader user having to endlessly tab through a long list of questions and answers in order to get to other content on the page?
Accordions give the visual user a clean and concise interface, with the accordion headers serving as an interactive outline for the hidden content. Having hidden content also benefits the screen reader user as it is not focusable and doesn’t force a user to tab through content in order to get to other areas of a webpage.
How to build out the accordion’s semantic structure?
Semantic HTML is the foundation of a truly accessible Web.Sara Soueidan, Developer and Teacher
In a fantastic blogpost titled “What a Year of Learning and Teaching Accessibility Taught Me,” developer Sara Soueidan emphasized the foundational importance of relying on semantic HTML. Why is semantic HTML important? HTML elements describe the content they are presenting, and “by using correct elements, your document content will have conveyable structure and meaning.”
When using native HTML elements, the DOM is able to convert the structure of these elements to an accessibility tree that provides information for assistive technologies. The screen reader will announce an element’s role, name, state, and value and because native elements have accessibility information already built in and available, it is important to rely on their semantics as much as possible. SOURCE
I knew that I would need a container to wrap around the sections of accordion headings and corresponding accordion panel content. I also figured I’d need a container to wrap around content, future proofing my accordion for the possibility of complex content requirements.
Now the problem with the code above is that headings are not designed to be interactive. Initially, they made sense to me because I knew that screen readers help a user navigate through a webpage according to its headings. SOURCE
After surveying multiple resources on accordion best practices, it seemed advisable to place a <button> tag as a child of the <h3> tag, allowing for the button text to become a replacement for the heading text. You don’t want to replace the heading for a button because this would limit a screen reader’s abilities to interact with the accordion heading content.
Developers should also avoid using a <div> tag with the role=”button” attribute because this approach removes information from the accessibility tree that a native HTML button provides. It is possible to make the <div> tag accessible but additional custom code would be required. Why not use what is already available and tested? SOURCE
This <button> tag can be easily styled by having it inherit the styles of the heading parent. I love this technique by Heydon Pickering because this avoids having to bloat your code undoing all of the native button styles. One important note: Make sure and add the focus style back into the button so that this is still visually available to users. Here’s the code for these techniques below:
How to indicate changes of state?
Because the accordion component expands and collapses content, it is necessary to both visually and programmatically demonstrate the changes in state. The user must be informed when the content is toggled between being available and hidden. Hidden content is not available to any user unless it is in an expanded state.
To demonstrate this toggling using visual cues, the accordion will show and hide a section of content when the accordion header is clicked. Often, this is represented by the animation of an icon indicating whether content is open or closed.
In order to communicate changes of state for screen reader users, a combination of WAI-ARIA attributes can be used. ARIA stands for Accessible Rich Internet Applications, and the range of aria attributes help make web content and web applications more accessible. They are designed to pick up where
The aria-expanded attribute on the button tells us if content is expanded or collapsed. SOURCE It’s important to note that developers often make the mistake of putting the aria-expanded attribute on the content itself, however, changes of state should always be placed on the control. SOURCE
The W3C’s ARIA resource details important information about the aria-expanded attribute. Essentially, the aria-expanded state “indicates whether the element, or another grouping element it controls, is currently expanded or collapsed.”
Therefore, if the aria-expanded state is false, “the element, or another grouping element it controls, is collapsed.” If the aria-expanded state is true, “the element, or another grouping element it controls, is expanded.” If the aria-expanded state is undefined, “the element, or another grouping element it controls, is neither expandable nor collapsible; all its child elements are shown or there are no child elements.” SOURCE
The aria-hidden attribute is often used to define the state of the accordion content. However, when set to false this attribute has been known to be a bit buggy in the browsers and it is advised not to rely on this attribute in production applications. SOURCE
Luckily, a native alternative, the hidden global HTML attribute, can now be successfully implemented across all browsers. This attribute is a boolean that determines if an element is or is not relevant to the web page. If an element has the hidden attribute the browser won’t render that element and the content will be hidden from all users. SOURCE
SVG icons for the visual user
The opening and closing of content is typically illustrated via the toggling of a plus and minus icon. For the visual user, the plus icon indicates that there is some additional content that can be opened. The minus icon indicates that the related section of content can be hidden.
For my example, I am relying on the svg icon suggested by Heydon Pickering. Here is his icon:
He does an excellent deep dive into the accessible implementation of this icon. For example, Pickering adds the attribute focusable=”false” since we don’t need the icon to be interactive. We only want the parent button to have this focused interaction.
Other tips that I really appreciated involved how to style the button. With the code below, the svg icon element will automatically inherit all of the styles of its parent, which in this case, is the heading.
We can ensure the elements respect high contrast themes. By applying a fill of currentColor to the elements, they change color with the surrounding text when it is affected by the theme change.Heydon Pickering, Inclusive Components
For additional styling choices and in depth explanations, please refer to Pickering’s section on collapsible content.
Where I landed, however, is mainly influenced by the chapter on Collapsible Sections in Heydon Pickering’s recently released book Inclusive Components. However, all of the authors did an excellent job presenting very similar arguments and approaches for accessible development. I highly recommend reading all of the resources to form your own opinion.
Here is what I preferred using for my accessible accordion:
What do I like about this approach? I love how the use of ES6 results with a very dry presentation. Basically, we are listening for clicks on every element with the .accordion__header class and then toggling our aria-expanded attributes and content panels. The call method lets us explicitly specify the value that this is equal to when looping through all of the content in the array.
Testing it on Voiceover
It is time to test my accordion on Voiceover. First, as a point of comparison, I wanted to see what a basic accordion presented like without accessibility given any consideration. Below is a short video of the W3 Schools accordion example:
The biggest issue here is there no indication of the state of the content. You can’t tell if the button is triggering an expanded or collapsed state for the panel content.
Now, let’s see the accordion I’ve put together in this post:
When using Voiceover it’s apparent what paying attention to the state of the accordion does for a screen reader user. While this accordion is visually the same to the W3 Schools accordion, it is much more accessible because it announces the collapsed and expanded states of the content.
One thing I’m going to change after listening to this accordion is my html setup. It bothers me that a user can’t tell how many panels are in the accordion. What if there were 30? That’s information I’d like to have from the beginning. So I’m going to wrap my accordion in an <ul> tag versus a <div> tag. I’m going to put the accordion header and panel into an <li> tag. This approach is suggested by Graham Armfield as well as Sarah Soueidan.
Eureka! Now when a screen reader accesses the component it will read off the number of items in the list. Listen below:
How to address performance with event delegation?
When I showed an initial draft of this post to my team’s FED (front-end) lead, he made the suggestion that I should address performance by considering event delegation. Why does event delegation help performance? Event delegation allows us to set up one event handler versus multiple. Because we only having one event versus multiple events, there is less strain on the memory, which results in better performance.
Event bubbling is a process that speaks to the order of events that are triggered in a parent and child relationship.
When an event is triggered on an element, for example a mouse click on a button, the same event is also triggered on all of that element’s ancestors. This process is known as event bubbling; the event bubbles up from the originating element to the top of the DOM tree. SOURCEAndrew Tetlaw, Sitepoint
The code above sets an event listener on the accordion <ul> element. When there is a click event it will run our accordion function. The accordion function identifies the target element using the closest method that grabs the element with the accordion-trigger attribute. I have placed this attribute on all the <button> elements as seen below.
When addressing the use case of multiple event listeners, as is required by the accordion component, a good rule of thumb is to use event delegation is to attach a single event listener on the parent element. This will prevent situations that drain memory, thus causing performance to lag.
Also, choosing event delegation for our accordion component also addresses issues regarding the dynamic aspect of data-driven approaches. Why? Because our event listener only checks for our .accordion__header selector when the click event fires. Not before. This allows us to dynamically add accordion content to the DOM, without being concerned that our event listener won’t catch them since the page already loaded. SOURCE
Here is a link to my Codepen with this final iteration (for today).
I found this research and writing exercise invaluable for mapping the accessible structure and function of the accordion component into my brain. It is always my desire to learn and teach at the same time. Not only has this been a good learning process for myself, but maybe it will help someone else synthesize the abundance of information out there.
This blogpost synthesized a lot of information from these amazing developers. I suggest you read their works as well for your own benefit.
- Graham Armfield, “My favorite accessible accordion pattern”
- Heydon Pickering, “Aria Controls are poop”
- Heydon Pickering, “Collapsible Content” in Inclusive Components
- Innovative Studio, “Web Accessibility Guidelines: Accordion”
- Official W3C documentation about aria-expand attribute
- Sara Soueidan, “How do you mark up an accordion?”
- Sara Soueidan, “What a Year Teaching and Learning Accessibility Taught Me”
- Andrew Tetlaw, “Event Delegation is Easier than You Think”