Multi-Level Accordion Menu

Multi-Level Accordion Menu

A simple CSS accordion menu with support for sub level items.

Nucleo icons

Sponsored by Nucleo, a free application to collect, customize and export all your icons as icon font and SVG symbols. Made by the CodyHouse folks!

All the resources available on CodyHouse are released under the BSD-3-Clause license. You can support our project with a Paypal donation 🙌

Today's resource is a handy accordion menu with support for groups/subitems. It works with CSS only, using the :checked pseudo-class selector on the checkboxes input elements. However we included a version with jQuery as well, in case you prefer a subtle animation compared to the instant default effect. Your call!

The first user case I can think of for this resources is a "layer organizer". Think of Sublime Text sidebar, or Photoshop layer window. Anyway, I'm sure you'll find a use for this new snippet to store in your arsenal ;)

Icons: Nucleoapp.com

Creating the structure

The HTML structure is pretty simple: the accordion is an unordered list. If a list item contains subitems, then we insert an input[type=checkbox] and its label. Also, we add the .has-children class to the list item. All "standard" list items contain just an anchor tag.

<ul class="cd-accordion-menu">
   <li class="has-children">
      <input type="checkbox" name ="group-1" id="group-1" checked>
      <label for="group-1">Group 1</label>

      <ul>
         <li class="has-children">
            <input type="checkbox" name ="sub-group-1" id="sub-group-1">
            <label for="sub-group-1">Sub Group 1</label>

            <ul>
               <li><a href="#0">Image</a></li>
               <li><a href="#0">Image</a></li>
               <li><a href="#0">Image</a></li>
            </ul>
         </li>
         <li><a href="#0">Image</a></li>
         <li><a href="#0">Image</a></li>
      </ul>
   </li>

   <li><a href="#0">Image</a></li>
   <li><a href="#0">Image</a></li>
</ul> <!-- cd-accordion-menu -->

Adding style

We use a smart (and quite standard nowadays) technique to detect the click and show sub content with CSS only: by including a checkbox input element, we can use the :checked pseudo-class and the adjacent sibling selector (div + div) to change the display mode of the sub <ul> element from "none" to "block".

Step by step: first of all, we have to make sure that the checkbox input element covers the entire list item that contains subitems. Put in other words: we need to create a custom checkbox. So, firstly, you need to make sure that when you click on the label, the checkbox is checked/unchecked as well. This is achieved by using the "for" attribute inside the label (label "for" attribute = input "name" and "id" attributes. See html section above). This way you can simply hide the input element and work with the label instead.

.cd-accordion-menu input[type=checkbox] {
  /* hide native checkbox */
  position: absolute;
  opacity: 0;
}
  .cd-accordion-menu label, .cd-accordion-menu a {
  position: relative;
  display: block;
  padding: 18px 18px 18px 64px;
  background: #4d5158;
  box-shadow: inset 0 -1px #555960;
  color: #ffffff;
  font-size: 1.6rem;
}

Now notice in the HTML structure that input, label and the unordered list (that we make visible on click) are siblings. By using the :checked pseudo-class, you can set the following process in motion: when the checkbox input is checked (click on label), then take the <ul> sibling element and change its display value from "none" to "block":

.cd-accordion-menu ul {
  /* by default hide all sub menus */
  display: none;
}

.cd-accordion-menu input[type=checkbox]:checked + label + ul,
.cd-accordion-menu input[type=checkbox]:checked + label:nth-of-type(n) + ul {
  /* use label:nth-of-type(n) to fix a bug on safari (<= 8.0.8) with multiple adjacent-sibling selectors*/
  /* show children when item is checked */
  display: block;
}

If you want to gently animate the opening phase, then include the .js file as well. Also remember to add the .animate class to the main .cd-accordion-menu element (this will animate the arrow rotation).

Join our newsletter

Get our monthly recap with the latest CodyHouse news