Accordion Menu

Building up a fully responsive accordion menu

Here at Pixel2HTML we like to give back to the open source community whenever we have the time. That’s why today we’re kickstarting our new Pixel2HTML Tutorials blog section with this post.

On this first release, we’re going to give you an overview of how to make a cool Responsive Mobile Menu with Accordions using technologies such as SASS, CoffeeScript, Jade, SVG icons and Gulp as our task runner.

You can check out our screencast on the following video, and check the step by step below as well.

Lets take a look at the completed site to get an overall picture:

A completed picture

Here a demo on GitHub Pages. Also remember that you can clone and checkout the repo for this project on GitHub to use on you own projects and/or to mess around with it.

The repo is actually a clone of my Ground-Zero package that I use for a lot of projects both personal and for Pixel2HTML

So after you clone the repo and run npm run firstTime and npm run getReady you should end with something like this:

Folder Structure

So let me give you a quick overview of the situation:

  • bower.json is where we are keeping track of our front-end tools, in this case we have jQuery and Modular Scale for Sass
  • Next we have our gulpfile.js file which only points to the gulp directory where we are keeping our gulp tasks to concatenate js files, compile sass, jade, etc.
  • node_modules is where the packages that node installs live
  • package.json is the file that keeps track of which node modules we are using for our project.
  • src inside this directory are all of our actual working files

src directory contents

Now lets get into the real deal lets code the accordion menu.

The whole idea is to take this kind of markup and turn into an accordion menu:

<nav id="main-nav">
  <div class="toggle"><span>Menu</span>
    <div class="burger">
      <div class="top"></div>
      <div class="middle"></div>
      <div class="bottom"></div>
    </div>
  </div>
  <div class="navigation">
    <ul class="firstLevel">
      <li><a title="First Level Link" href="#" title="Top Level Link">First Level Link</a><span class="more-toggle">
          <svg class="icon arrow">
            <use xlink:href="#arrow"></use>
          </svg></span>
        <ul class="secondLevel">
          <li><span class="even-more-toggle">
              <svg class="icon plus">
                <use xlink:href="#plus"></use>
              </svg></span><a title="Second Level Link" href="#" title="Second Level Link">Second Level Link</a>
            <ul class="thirdLevel">
              <li><a title="Third Level Link" href="#" title="Third Level Link">Third Level Link</a></li>
            </ul>
          </li>
        </ul>
      </li>
    </ul>
  </div>
</nav>

Moving forward I’ll use the Jade notation because its much cleaner. For more info check Jade-Lang.

So the basic idea is we have our nav inside we get the .toggle which contains the burger icon as well as the word “Menu” then we get the actual .navigation which inside has an unordered list with some more unordered lists nested inside (those will work as our accordion stuff)

SVG Icons

For our icons we are using some inline svgs. I’ve abstracted the work away in such a way that you only need to drop the icons you want inlined inside src/svg/inline but if you want to know the logic behind that check this article where I got the inspiration for such thing: How to work with SVG icons

Also here is the correspondent gulp task of the svgs: Svg.coffee

The actual accordion

Now for the accordion part what we actually need is some stuff:

  • Start with the item you want to toggle with a display: none style. So for us that means hiding the entire .navigation as well as the .secondLevel and .thirdLevel classes.

On my CSS I’m also creating a new class called .is-active which just sets the display to block lets look at the navigation example:

.navigation
    display: none
    position: absolute
    right: 0
    background: $white
    color: $black
    top: 100%
    width: 80%

    +is(active)
      display: block

Now on our javascript we are going to use jQuery’s .slideUp() and .slideDown() methods for the animation as such:

# Main toggle is the orange Menu with hamburger icon
mainToggle = $('#main-nav .toggle')
# Navigation is the actual box with the links and stuff
navigation = $('#main-nav .navigation')

# On click we display or hide the navigation. Easy to grasp :)
mainToggle.on 'click', ->
  mainToggle.toggleClass 'is-triggered'
  if ( navigation.hasClass 'is-active' )
    navigation.slideUp()
    navigation.toggleClass 'is-active'
  else
    navigation.slideDown()
    navigation.toggleClass 'is-active'

When we click the toggle we will trigger the class is-triggered on the toggle, that will make the transition for the burger icon to become an X icon. Next we will check if the navigation is active and either add. Or remove the is-active class as well as performing the slideUp or slideDown method.

Now for the accordion part of the nested second level ul tags:

# This toggles are the ones with the arrows pointing down
moreToggle = $('#main-nav .more-toggle')
# When any of those ones is clicked
moreToggle.on 'click', ->
  # First we define actually which one was clicked to perform stuff on it and we name it that
  that = $(this)
  # Then we transverse the DOM to find its correspondent <ul> tag with class .secondLevel
  secondLevel = that.siblings '.secondLevel'
  # We also need all of the other ones
  allSeconds = $('#main-nav .secondLevel')
  # If our current toggle is active, remove the class and also make small all of the second level items
  if (that.hasClass 'is-active')
    secondLevel.slideUp()
    that.removeClass 'is-active'
    moreToggle.removeClass 'is-active'
    allSeconds.removeClass 'is-active'
    allSeconds.slideUp()
  # else we remove the class and minimize everything else except the ones we care about
  else
    moreToggle.removeClass 'is-active'
    allSeconds.removeClass 'is-active'
    allSeconds.slideUp()
    secondLevel.slideDown()
    that.addClass 'is-active'
    secondLevel.addClass 'is-active'

We are almost doing the same except we are only allowing one nested ul tag to be open at a time.

For the third nested ul tag we are doing the same with one exception. Since we are also changing the icon from a + to a – we need to do that with JavaScript as well as such:

# Now we basically do the same for the third nested UL with class .thirdLevel
evenMoreToggle = $('#main-nav .even-more-toggle')

evenMoreToggle.on 'click', ->
  that = $(this)
  thirdLevel = that.siblings '.thirdLevel'
  allThirds = $('#main-nav .thirdLevel')
  allSVGs = $('#main-nav .plus').children()
  SVG = that.find('use')


  # So if the third level is active we want to make all of the levels not-active and also return to our plus icons
  if ( thirdLevel.hasClass 'is-active')
    allThirds.slideUp()
    allThirds.removeClass 'is-active'
    allSVGs.attr 'xlink:href', '#plus'

  else
    # Removes the Minus off all SVGs
    allSVGs.attr 'xlink:href', '#plus'
    # Adds the minus to our SVG
    SVG.attr 'xlink:href', '#minus'

    allThirds.slideUp()
    thirdLevel.slideDown()
    allThirds.removeClass 'is-active'
    thirdLevel.addClass 'is-active'

But the difference is that we are using the .attr jQuery method to set the icon xlink:href attribute to either #plus or #minus depending on the situation.

And that’s it! You should be all set by now.

Remember to take a look at the source code over our Github account and at the demo for a more detailed overview.



Any questions, fee free to reach us on our twitter account @Pixel2HTML and you can follow me as well at @mpalau.

And if you need help with your Front-End Development, just contact us!





Do you want us to help you build your project?

Get it started