Recently, a colleague inspired me by a challenge to live code an accordion component relying on the basics: HTML, CSS, and Vanilla Javascript. I decided to turn this task into a research project because I wanted to understand, not just how to create an accordion for the visual user, but also for users who rely on assistive technologies such as screen readers.

Basically, I wanted to consider accessibility or “A11y” when building this component.

I broke down my research approach this way:

  1. 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?
  2. BUILD OUT SEMANTIC HTML STRUCTURE: Good accessibility requires reliance on semantic HTML whenever possible. What would that look like?
  3. 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.
  4. APPLY JAVASCRIPT: Accordions I have interacted with thus far have all used Javascript to magically show and hide content from the DOM. I assumed I would use Javascript but wasn’t sure if that would be the best approach for accessibility? Would a CSS only accordion be better since some users with disabilities turn off Javascript in the browser?
  5. 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

Chrome devtools accessibility tree exposed for accordion
Notice all of the information available in the accessibility tree (on the right).

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.

basic accordion HTML code
Basic accordion HTML structure

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

accordion code that shows inclusion of button elements
Make the accordion headers interactive.

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:

button styles
Button styles – remember to add in focus!

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.

While CSS only accordions can communicate the expanding and collapsing of content, they cannot communicate to the screen reader user the changes of state. This is where Javascript becomes a useful tool to make an accordion component accessible because it can toggle the attributes which indicate to the screen reader whether content is expanded or collapsed.


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

hidden attribute

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

accordion code showing inclusion of aria and hidden attributes
Add WAI-ARIA and HTML global attributes for accessibility.

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:

svg icon code by Heydon Pickering
Heydon Pickering’s svg 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
Code to style the svg icon.

For additional styling choices and in depth explanations, please refer to Pickering’s section on collapsible content.

How to choose the Javascript for my accordion component?

I want to be clear that I looked at a lot of resources to create an accessible component. The following articles were very thorough and helped me grasp the important concepts as well as illustrating how to code an accessible accordion. I read “Javascript and Accessibility: Accordions” from the blog Ally with Lindsey, “My favorite accessible accordion pattern” by Graham Armfield, and “Web Accessibility Guidelines: Accordions” from the Innovation Studio at the Carnegie Museums of Pittsburgh.

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:

javascript for accordion based on Heydon Pickering inclusive components
Add the Javascript functionality.

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:

W3 Schools 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:

My accessible accordion example

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.

accordion code with added ul and li tags
Wrapping the accordion in an unordered list.

Eureka! Now when a screen reader accesses the component it will read off the number of items in the list. Listen below:

An accessible component that is within an unordered list.

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.

Essentially, the above code attaches an event listener on every element with the .accordion__header class. We can actually refactor this accordion so the there is only one event listener on the parent element, the <ul> tag itself. Why is this possible and how does event delegation work? To understand this we need to first conceptualize the following Javascript features: event bubbling and the target event.

event bubbling

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. SOURCE

Andrew Tetlaw, Sitepoint
event bubbling illustration
Event Bubbling

target element

The other aspect of event delegation involves the target element feature of Javascript. What is a target element? A target element is the originating element that initiates the event. For the purposes of our accordion, it is important to recognize that a target element can also be the ancestor of the javascript event, in our case the <ul> tag that is wrapping our accordian headers and panels.

event delegation

Using both event bubbling and the target element, we can now integrate a javascript approach for the accordion component that utilizes event delegation. Basically, when the click event on the <button> element bubbles to the accordion’s parent <ul> tag, “you check the event object’s target property to gain a reference to the actual clicked node.” SOURCE

accordion javascript with event delegation implemented
Refactored accordion javascript to implement event delegation.

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.

html accordion code with accordion-trigger attribute added to button tags
Add the accordion-trigger attribute to <button> tags.

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).

Final Thoughts

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 accordion I finished here is not yet progressively enhanced. If Javascript is turned off someone’s browser the hidden content would remain hidden. This is a problem that I will continue researching. Both Heydon Pickering and the author from A11y with Lindsey take different approaches to having the content available in this scenario. One of my collegues suggested using the <datails> and <summary> elements and working backwards from there. I will be looking into this and will update this post at a later time.


This blogpost synthesized a lot of information from these amazing developers. I suggest you read their works as well for your own benefit.