Multi-Level Sliding Accordion Only with CSS

Multi-Level Sliding Accordion Only with CSS
Reading Time: 12 minutes

Have you ever wanted to create sliding multi-level accordion without the need to rely on JavaScript? Well, today is your lucky day. In this tutorial I will show you how to use your HTML and CSS skills to achieve this feat and improve browsing experience on your website or project. Again, no JavaScript is needed. So, are you ready?

You can see the accordion in action on Codepen and download all files from Github repository.

Gathering Dependencies

As always we will start with putting together all assets we will need to get this tutorial working. Fortunately, the only thing you will need is font awesome to render angle icons I used in accordion, and also the links in footer. The font used for labels and links is “Open sans” hosted on Google fonts CDN. You have two options. The last two things I used in this tutorial are reset stylesheet and autoprefixer plugin. It is up to you whether will you download the assets described or if you will use CDN and just include the links in your project.

Creating Accordion Structure

When you have the assets prepared, it’s time to move to HTML and create structure for the accordion. The accordion will be wrapped in header and nav element. It will compose of a single unordered list containing four list items. Each of these items will contain checkbox input with id set to “group-*” (* is for 1,2,3 and 4) and attribute “hidden” and label with for attribute and text “First level” or whatever you want. The label will also include span element as a placeholder for right angle icon provided by font awesome. Pay attention to use unique ids and for attribute for every checkbox and label. Otherwise, you will activate (check) checkboxes you don’t want.

Note: make sure to always put checkbox before its label. Otherwise, you will not be able to open and close the sub-lists later through CSS.

<header role="banner">
 <nav class="nav" role="navigation">
  <ul class="nav__list">
   <li>
    <input id="group-1" type="checkbox" hidden />
    <label for="group-1"><span class="fa fa-angle-right"></span> First level</label>
   </li>
   <li>
    <input id="group-2" type="checkbox" hidden />
    <label for="group-2"><span class="fa fa-angle-right"></span> First level</label>
   </li>
   <li>
    <input id="group-3" type="checkbox" hidden />
    <label for="group-3"><span class="fa fa-angle-right"></span> First level</label>
   </li>
   <li>
    <input id="group-4" type="checkbox" hidden />
    <label for="group-4"><span class="fa fa-angle-right"></span> First level</label>
   </li>
  </ul>
 </nav>
</header>

The structure of individual list items will depend on what do you need or want to show in the navigation. For this tutorial, let’s say the first list item should contain one link, then nested list with another three links and one more nested list with three links.

The first child of the list item will be again checkbox followed by label. I will use id “group-1” for checkbox and the same value for for attribute of label. After the label will be placed nested list for second level. This list has classgroup-list” and contains two list items. First item contains link with text “1st level itemand second checkbox, label and another nested list, now with class “sub-group-list”. Inside this “sub-group-list” four list items. Three of them contains links with text “2nd level nav item” and the last one contains nested list with classsub-sub-group-list”. Inside this list are three list items all containing links with text “3rd level nav item”.

List item example:

<li>
 <input id="group-1" type="checkbox" hidden />
 <label for="group-1"><span class="fa fa-angle-right"></span> First level</label>
 <ul class=”group-list">
  <li><a href="#">1st level item</a></li>
  <li>
   <input id="sub-group-1" type="checkbox" hidden />
   <label for="sub-group-1"><span class="fa fa-angle-right"></span> Second level</label>
   <ul class="sub-group-list">
    <li><a href="#">2nd level nav item</a></li>
    <li><a href="#">2nd level nav item</a></li>
    <li><a href="#">2nd level nav item</a></li>
    <li>
     <input id="sub-sub-group-1" type="checkbox" hidden />
     <label for="sub-sub-group-1"><span class="fa fa-angle-right"></span> Third level</label>
     <ul class="sub-sub-group-list">
      <li><a href="#">3rd level nav item</a></li>
      <li><a href="#">3rd level nav item</a></li>
      <li><a href="#">3rd level nav item</a></li>
     </ul>
    </li>
   </ul>
  </li>
 </ul>
</li>

Next, you can either copy the code above and paste it couple times to create navigation with consistent content for each list item or implement light variation like I did and add or remove elements here and there. Anyway complete HTML structure for accordion with four list items of first level is following.

Note: footer component is just a small perk and not necessary :).

<header role="banner">
 <nav class="nav" role="navigation">
   <ul class="nav__list">
    <li>
     <input id="group-1" type="checkbox" hidden />
     <label for="group-1"><span class="fa fa-angle-right"></span> First level</label>
     <ul class="group-list">
      <li><a href="#">1st level item</a></li>
      <li>
       <input id="sub-group-1" type="checkbox" hidden />
       <label for="sub-group-1"><span class="fa fa-angle-right"></span> Second level</label>
       <ul class="sub-group-list">
        <li><a href="#">2nd level nav item</a></li>
        <li><a href="#">2nd level nav item</a></li>
        <li><a href="#">2nd level nav item</a></li>
        <li>
         <input id="sub-sub-group-1" type="checkbox" hidden />
         <label for="sub-sub-group-1"><span class="fa fa-angle-right"></span> Third level</label>
         <ul class="sub-sub-group-list">
          <li><a href="#">3rd level nav item</a></li>
          <li><a href="#">3rd level nav item</a></li>
          <li><a href="#">3rd level nav item</a></li>
         </ul>
        </li>
       </ul>
      </li>
     </ul>
    </li>
    <li>
     <input id="group-2" type="checkbox" hidden />
     <label for="group-2"><span class="fa fa-angle-right"></span> First level</label>
     <ul class="group-list">
      <li>
       <li><a href="#">1st level item</a></li>
       <li><a href="#">1st level item</a></li>
       <input id="sub-group-2" type="checkbox" hidden />
       <label for="sub-group-2"><span class="fa fa-angle-right"></span> Second level</label>
       <ul class="sub-group-list">
        <li><a href="#">2nd level nav item</a></li>
        <li><a href="#">2nd level nav item</a></li>
        <li>
         <input id="sub-sub-group-2" type="checkbox" hidden />
         <label for="sub-sub-group-2"><span class="fa fa-angle-right"></span> Third level</label>
         <ul class="sub-sub-group-list">
          <li><a href="#">3rd level nav item</a></li>
         </ul>
        </li>
       </ul>
      </li>
     </ul>
    </li>
    <li>
     <input id="group-3" type="checkbox" hidden />
     <label for="group-3"><span class="fa fa-angle-right"></span> First level</label>
     <ul class="group-list">
      <li>
       <li><a href="#">1st level item</a></li>
       <li><a href="#">1st level item</a></li>
       <input id="sub-group-3" type="checkbox" hidden />
       <label for="sub-group-3"><span class="fa fa-angle-right"></span> Second level</label>
       <ul class="sub-group-list">
        <li><a href="#">2nd level nav item</a></li>
        <li><a href="#">2nd level nav item</a></li>
        <li><a href="#">2nd level nav item</a></li>
        <li>
         <input id="sub-sub-group-3" type="checkbox" hidden />
         <label for="sub-sub-group-3"><span class="fa fa-angle-right"></span> Third level</label>
         <ul class="sub-sub-group-list">
          <li><a href="#">3rd level nav item</a></li>
          <li><a href="#">3rd level nav item</a></li>
          <li><a href="#">3rd level nav item</a></li>
         </ul>
        </li>
       </ul>
      </li>
     </ul>
    </li>
    <li>
     <input id="group-4" type="checkbox" hidden />
     <label for="group-4"><span class="fa fa-angle-right"></span> First level</label>
     <ul class="group-list">
      <li>
       <li><a href="#">1st level item</a></li>
       <input id="sub-group-4" type="checkbox" hidden />
       <label for="sub-group-4"><span class="fa fa-angle-right"></span> Second level</label>
       <ul class="sub-group-list">
        <li><a href="#">2nd level nav item</a></li>
        <li><a href="#">2nd level nav item</a></li>
       </ul>
      </li>
     </ul>
    </li>
   </ul>
 </nav>
 <footer>
  <ul class="soc-media">
   <li><a href="https://twitter.com/alexdevero" target="_blank"><span class="fa fa-twitter"></span></a></li>
   <li><a href="https://www.facebook.com/deveroalex" target="_blank"><span class="fa fa-facebook"></span></a></li>
   <li><a href="https://plus.google.com/+AlexDevero" target="_blank"><span class="fa fa-google-plus"></span></a></li>
   <li><a href="http://blog.alexdevero.com" target="_blank"><span class="fa fa-globe"></span></a></li>
  </ul>
 </footer>
</header>

Adding Style

Next and also the last step of this tutorial is to style the accordion and create mechanism for nested lists. The first thing we will begin with is setting font to “100% ‘Open sans’, ‘Trebuchet MS’, sans-serif” via styling the body element, removing underline from links and creating fallback for older browsers not supporting hidden attribute.

body {
 margin-top: 2rem; /* Just for demo on Codepen */
 font: 100% "Open sans", "Trebuchet MS", sans-serif;
}
a {text-decoration: none;}

/**
 * Hidden fallback
 */
[hidden] {
 display: none;
 visibility: hidden;
}

Depending on how do you want to use this tutorial you can either follow styling of the header or skip it. Styles below are to create a nice centered rectangular box with light box shadow.

/**
 * Styling navigation
 */
header {
 margin-right: auto;
 margin-left: auto;
 max-width: remy(360px);
 box-shadow: 0 3px 12px rgba(0,0,0,.25);
}

Anyway, let’s style the links and labels on the top level. We have to set the display property to “block”, padding to “.85rem”, color to “#fff”, background-color to “#151515”, box-shadow to “inset 0 -1px #1d1d1d” and transition to “all .25s ease-in”. Box-shadow is used to create a tiny divider between individual links and labels. We will also create styles for :focus and :hover states that will include change in color and background-color. Also, let’s not forget to imitate link behavior for labels by setting cursor property to “pointer”.

/**
 * Styling top level items
 */
.nav a,
.nav label {
 display: block;
 padding: .85rem;
 color: #fff;
 background-color: #151515;
 box-shadow: inset 0 -1px #1d1d1d;
 -webkit-transition: all .25s ease-in;
 transition: all .25s ease-in;
}
.nav a:focus,
.nav a:hover,
.nav label:focus,
.nav label:hover {
 color: rgba(255, 255, 255, 0.5);
 background-color: #030303;
}
.nav label {
 cursor: pointer;
}

Styling elements on the first level will require change background-color and box-shadow (for “dividers”) to distinguish them visually from the rest of the pack. You can also create small indentation by using left padding like I did. Also, include styles for :focus and :hover states.

/**
 * Styling first level lists items
 */
.group-list a,
.group-list label {
 padding-left: 2rem;
 background-color: #252525;
 box-shadow: inset 0 -1px #373737;
}
.group-list a:focus,
.group-list a:hover,
.group-list label:focus,
.group-list label:hover {
 background-color: #131313;
}

Styles for second and third level elements will follow the same fashion as styles for the first level. You will only change background-color, box-shadow and you can also use padding for indentation.

/**
 * Styling second level list items
 */
.sub-group-list a,
.sub-group-list label {
 padding-left: 4rem;
 background-color: #353535;
 box-shadow: inset 0 -1px #474747;
}
.sub-group-list a:focus, .sub-group-list a:hover,
.sub-group-list label:focus,
.sub-group-list label:hover {
 background-color: #232323;
}
/**
 * Styling third level list items
 */
.sub-sub-group-list a,
.sub-sub-group-list label {
 padding-left: 6rem;
 background-color: #454545;
 box-shadow: inset 0 -1px #575757;
}
.sub-sub-group-list a:focus, .sub-sub-group-list a:hover,
.sub-sub-group-list label:focus,
.sub-sub-group-list label:hover {
 background-color: #333333;
}

Now, it’s time to hide the nested lists. This will be done by setting max-height to “0” while height to “100%” and also setting overflow to “hidden”. We will also use transition for max-height to create sliding effect later on.

/**
 * Hide nested lists
 */
.group-list,
.sub-group-list,
.sub-sub-group-list {
 height: 100%;
 max-height: 0;
 overflow: hidden;
 -webkit-transition: max-height .5s ease-in-out;
 transition: max-height .5s ease-in-out;
}

With this setting on place, let’s create the magic for showing nested lists after clicking on labels. This will be very simple. We will do only one thing and that will be setting max-height of lists to some pretty big number.

.nav__list input[type=checkbox]:checked + label + ul {
 /* reset the height when checkbox is checked */
 max-height: 1000px;
}

The last thing to make your accordion classy is to float angle icons to the right and create some smooth animation. Sure, you can leave them as they are if you want. Anyway, the animation is simple. Just use transform property set to “rotate(90deg)” when checkbox is checked and also don’t forget transition set to “transform .65s ease. This way, every time you will open one of the nested lists, the angle icon will nicely rotate for 90 degrees and return to its default state when you close the list.

/**
 * Rotating chevron icon
 */
label > span {
 float: right;
 -webkit-transition: -webkit-transform .65s ease;
 transition: transform .65s ease;
}
.nav__list input[type=checkbox]:checked + label > span {
 -webkit-transform: rotate(90deg);
 -ms-transform: rotate(90deg);
 transform: rotate(90deg);
}

Since the footer component is not the main purpose of this tutorial I will only include the necessary styles in complete code.

Complete sass version:

// Remy function
@function remy($value) {
 @return ($value / 16px) * 1rem;
}
body {
 margin-top: 2rem;
 font: 100% "Open sans", "Trebuchet MS", sans-serif;
}
a {text-decoration: none;}

/**
 * Hidden fallback
 */
[hidden] {
 display: none;
 visibility: hidden;
}

/**
 * Styling navigation
 */
header {
 margin-right: auto;
 margin-left: auto;
 max-width: remy(360px);
 box-shadow: 0 3px 12px rgba(0,0,0,.25);
}
/**
 * Styling top level items
 */
.nav a,
.nav label {
 display: block;
 padding: .85rem;
 color: #fff;
 background-color: #151515;
 box-shadow: inset 0 -1px lighten(#151515, 3%);
 transition: all .25s ease-in;
 &:focus,
 &:hover {
  color: rgba(255,255,255,.5);
  background-color: darken(#151515, 7%);
 }
}
.nav label {cursor: pointer;}

/**
 * Styling first level lists items
 */
.sub-group-list a,
.sub-group-list label {
 padding-left: 2rem;
 background-color: #252525;
 box-shadow: inset 0 -1px lighten(#252525, 7%);
 &:focus,
 &:hover {background-color: darken(#252525, 7%);}
}

/**
 * Styling second level list items
 */
.sub-group-list__list a,
.sub-group-list__list label {
 padding-left: 4rem;
 background-color: #353535;
 box-shadow: inset 0 -1px lighten(#353535, 7%);
 &:focus,
 &:hover {background-color: darken(#353535, 7%);}
}

/**
 * Styling third level list items
 */
.sub-sub-group-list__list a,
.sub-sub-group-list__list label {
 padding-left: 6rem;
 background-color: #454545;
 box-shadow: inset 0 -1px lighten(#454545, 7%);
 &:focus,
 &:hover {background-color: darken(#454545, 7%);}
}

/**
 * Hide nested lists
 */
.sub-group-list,
.sub-group-list__list,
.sub-sub-group-list__list {
 height: 100%;
 max-height: 0;
 overflow: hidden;
 transition: max-height .5s ease-in-out;
}
 
.nav__list input[type=checkbox]:checked + label + ul {
 /* reset the height when checkbox is checked */
 max-height: 1000px;
}

/**
 * Rotating chevron icon
 */
label > span {
 float: right;
 transition: transform .65s ease;
}
.nav__list input[type=checkbox]:checked + label > span {transform: rotate(90deg);}
/**
 * Styling footer
 */
footer {
 padding-top: 1rem;
 padding-bottom: 1rem;
 background-color: #050505;
}
.soc-media {
 display: flex;
 justify-content: center;
}
.soc-media li:nth-child(n+2) {margin-left: 1rem;}
.soc-media a {
 font-size: 1.25rem;
 color: rgba(255,255,255,.65);
 transition: color .25s ease-in;
 &:focus,
 &:hover {color: rgba(255,255,255,.2);}
}

or CSS version:

body {
 margin-top: 2rem;
 font: 100% "Open sans", "Trebuchet MS", sans-serif;
}
a {
 text-decoration: none;
}

/**
 * Hidden fallback
 */
[hidden] {
 display: none;
 visibility: hidden;
}

/**
 * Styling navigation
 */
header {
 margin-right: auto;
 margin-left: auto;
 max-width: 22.5rem;
 box-shadow: 0 3px 12px rgba(0, 0, 0, 0.25);
}

/**
 * Styling top level items
 */
.nav a,
.nav label {
 display: block;
 padding: .85rem;
 color: #fff;
 background-color: #151515;
 box-shadow: inset 0 -1px #1d1d1d;
 -webkit-transition: all .25s ease-in;
 transition: all .25s ease-in;
}
.nav a:focus, .nav a:hover,
.nav label:focus,
.nav label:hover {
 color: rgba(255, 255, 255, 0.5);
 background-color: #030303;
}
.nav label {
 cursor: pointer;
}

/**
 * Styling first level lists items
 */
.sub-group-list a,
.sub-group-list label {
 padding-left: 2rem;
 background-color: #252525;
 box-shadow: inset 0 -1px #373737;
}
.sub-group-list a:focus, .sub-group-list a:hover,
.sub-group-list label:focus,
.sub-group-list label:hover {
 background-color: #131313;
}

/**
 * Styling second level list items
 */
.sub-group-list__list a,
.sub-group-list__list label {
 padding-left: 4rem;
 background-color: #353535;
 box-shadow: inset 0 -1px #474747;
}
.sub-group-list__list a:focus, .sub-group-list__list a:hover,
.sub-group-list__list label:focus,
.sub-group-list__list label:hover {
 background-color: #232323;
}

/**
 * Styling third level list items
 */
.sub-sub-group-list__list a,
.sub-sub-group-list__list label {
 padding-left: 6rem;
 background-color: #454545;
 box-shadow: inset 0 -1px #575757;
}
.sub-sub-group-list__list a:focus, .sub-sub-group-list__list a:hover,
.sub-sub-group-list__list label:focus,
.sub-sub-group-list__list label:hover {
 background-color: #333333;
}

/**
 * Hide nested lists
 */
.sub-group-list,
.sub-group-list__list,
.sub-sub-group-list__list {
 height: 100%;
 max-height: 0;
 overflow: hidden;
 -webkit-transition: max-height .5s ease-in-out;
 transition: max-height .5s ease-in-out;
}
.nav__list input[type=checkbox]:checked + label + ul {
 /* reset the height when checkbox is checked */
 max-height: 1000px;
}

/**
 * Rotating chevron icon
 */
label > span {
 float: right;
 -webkit-transition: -webkit-transform .65s ease;
 transition: transform .65s ease;
}
.nav__list input[type=checkbox]:checked + label > span {
 -webkit-transform: rotate(90deg);
 -ms-transform: rotate(90deg);
 transform: rotate(90deg);
}

/**
 * Styling footer
 */
footer {
 padding-top: 1rem;
 padding-bottom: 1rem;
 background-color: #050505;
}
.soc-media {
 display: -webkit-box;
 display: -webkit-flex;
 display: -ms-flexbox;
 display: flex;
 -webkit-box-pack: center;
 -webkit-justify-content: center;
 -ms-flex-pack: center;
 justify-content: center;
}
.soc-media li:nth-child(n+2) {
 margin-left: 1rem;
}
.soc-media a {
 font-size: 1.25rem;
 color: rgba(255, 255, 255, 0.65);
 -webkit-transition: color .25s ease-in;
 transition: color .25s ease-in;
}
.soc-media a:focus, .soc-media a:hover {
 color: rgba(255, 255, 255, 0.2);
}

Final Words

You did it again! Following the instructions and code examples above you created fully working multi-level accordion only with HTML and CSS! The best thing is that this accordion doesn’t depend on single line of JavaScript and will work in all modern browsers. The only thing that can break is the transition effect for rotating angle icons and expanding lists because CSS transitions are not supported by IE9 and lower. However, any browser more up-to-date than that will render all transitions smoothly. Now, use what you’ve learned and show it to the world.

1 Comment

  1. Hi

    Is it possibly to have the arrows on the left side.
    If you have the menu on the right side instead of the left side.

    Anyway, it looks good

    Regards
    Ib

Comments are closed.