DEV Community

loading...
Cover image for Creating Accessible Accordions with HTML, CSS & JavaScript

Creating Accessible Accordions with HTML, CSS & JavaScript

lizlaffitte profile image Liz Laffitte ・4 min read

An accordion, in development and design, is a graphical control element that consists of vertically stacked headers and hidden internal content. When clicked, a header's previously collapsed/hidden content box will expand to show its content; often text, images, or other grouped information.

You've probably seen (or used) an accordion on a FAQ page, with the questions shown in the headers, and the answers to those questions hidden in the content boxes.

Accordions can help increase the user experience on web and application pages with lots of information. They allow developers to group all that information on one page, but only display the higher level headers/titles. Users can then glance over all the titles without being overwhelmed by the details. They can more easily find, and click on, the headers/titles that they are interested in, and access the greater detail of the content.

There are countless widgets, plugins and other code snippets that will auto-magically add an accordion to your website or app. But you can also build a simple accordion with only HTML, CSS and JavaScript.

Accordion HTML

<ul id="accordion">
  <li>
    <button aria-controls="content-1" aria-expanded="false" id="accordion-control-1">FAQ 1</button>
    <div class="acc-item-content" aria-hidden="true" id="content-1">
      <p>Answer 1!</p>
    </div>
  </li>
  <li>
    <button aria-controls="content-2" aria-expanded="false" id="accordion-control-2">FAQ 2</button>
    <div class="acc-item-content" aria-hidden="true" id="content-2">
      <p>Answer 2</p>
    </div>
  </li>
  <li>
    <button aria-controls="content-3" aria-expanded="false" id="accordion-control-3">FAQ 3</button>
    <div class="acc-item-content" aria-hidden="true" id="content-3">
      <p>Answer 3</p>
    </div>
  </li>
  <li>
    <button aria-controls="content-4" aria-expanded="false" id="accordion-control-4">FAQ 4 </button>
    <div class="acc-item-content" aria-hidden="true" id="content-4">
      <p>Answer 4</p>
    </div>
  </li>
  <li>
    <button aria-controls="content-5" aria-expanded="false" id="accordion-control-5">FAQ 5</button>
    <div class="acc-item-content" aria-hidden="true" id="content-5">
      <p>Answer 5</p>
    </div>
  </li>
</ul>
Enter fullscreen mode Exit fullscreen mode

For the HTML, our entire accordion is housed in an unordered list. Each list item holds a div with the inner content and a button that will toggle the div's visibility. In an effort to make the accordion more accessible, we have aria-expanded and aria-hidden attributes, as well as aria-controls attributes on the buttons that correspond with the ids of the acc-item-content divs. These attributes will help users using screen readers understand our accordion, and what is and is not visible when the buttons are clicked on.

I've also got my text in paragraph tags, which will be helpful if you have more than a few sentences in the content divs.

Hopefully you're using a loop somewhere to dynamically create each list item and its child elements.

Accordion CSS

ul {
  list-style: none;
}

#accordion button:focus {
  border-radius: 0px;
  outline: none;
}
#accordion button {
  outline: none;
  background-color: DarkSeaGreen;
  padding: 10px;
  border: none;
  border-bottom: 1px solid darkslategrey;
  color: white;
  width: 100%;
  text-align: left;
  font-size: 16px;
  border-radius: 0px;
}
#accordion li {
  border: 1px solid DarkSlateGray;
  border-bottom: none;
}
.acc-item:last-child {
  border-bottom: 1px solid DarkSlateGray;
}
#accordion button::after {
  content: "\002B";
  font-weight: 900;
  font-size: 22px;
  float: right;
}

#accordion {
  width: 80%;
  max-width: 800px;
  min-width: 275px;
  margin: auto;
}

Enter fullscreen mode Exit fullscreen mode

Most of the CSS is for...style. We add background colors, borders and pseudo content to visually indicate that this is an accordion, and that you should click if you want to see more.

Technically, the only rule set you need is this one:

.acc-item-content {
  padding: 0px 10px;
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.3s ease-out;
}
Enter fullscreen mode Exit fullscreen mode

It sets the height of the content divs to 0 (hiding them from view); and gives the max-height a transition style and speed. This will come in handy when we get to the JavaScript, where we'll change the max-height values for our divs when the buttons are clicked.

Accordion JavaScript

window.addEventListener("DOMContentLoaded", (event) => {
  let buttons = document.querySelectorAll("#accordion button");
  buttons.forEach((button) => {
    let content = button.nextElementSibling;
    button.addEventListener("click", (event) => {
      if (button.classList.contains("active")) {
        button.classList.remove("active");
        button.setAttribute("aria-expanded", false);
        content.style.maxHeight = null;
        content.setAttribute("aria-hidden", true);
      } else {
        button.classList.add("active");
        button.setAttribute("aria-expanded", true);
        content.style.maxHeight = content.scrollHeight + "px";
        content.setAttribute("aria-hidden", false);
      }
    });
  });
});
Enter fullscreen mode Exit fullscreen mode

In pseudo code:

When all the DOM content is loaded...

  Collect all the buttons that are child elements of the element 
  with the id #accordion...

  Loop through each of these buttons...
     Grab the button's sibling element and save it in a variable 
     called content AND

     Add an event listener to each button, so that when the 
     button is clicked...

       If the button has the class active...
           Remove "active" from its class list AND

           Set its aria-expanded attribute to false AND

           Set the content variable's max-height value to null AND

           Set the content variable's aria-hidden attribute to true.

       Otherwise, if the button doesn't have the class active...
            Add "active" to its class list AND

           Set its aria-expanded attribute to true AND

           Set the content variable's max-height value even 
           to the value of the content variable's scroll height 
           (the height of an element's content) AND

           Set the content variable's aria-hidden attribute to false.
Enter fullscreen mode Exit fullscreen mode

And that's it: an accessible, simple accordion made with only HTML, CSS and vanilla JavaScript!

Discussion (0)

pic
Editor guide