Animated Drop Down Menu With CSS3 (and no JavaScript)

Some thing quick, simple and fun this time.   A drop down menu, built solely with HTML and CSS, that is functional back to IE7, and animated for browsers that support CSS3 transitions. See the end-product, here.  Let’s get going…

First, the HTML structure.


<menu id="mainmenu">
  <ul>
    <li>
      <a href="#">Main 1</a>
      <ul>
        <li><a href="#">Sub Menu 1-1</a></li>
        <li><a href="#">Sub Menu 1-2this a long one goes for a while</a></li>
        <li><a href="#">Sub Menu 1-3</a></li>
      </ul>
    </li>
    <li>
      <a href="#">Main 2</a>
      <ul>
        <li><a href="#">Sub Menu 2-1</a></li>
        <li><a href="#">Sub Menu 2-2</a></li>
        <li><a href="#">Sub Menu 2-3</a></li>
        <li><a href="#">Sub Menu 2-4</a></li>
        <li><a href="#">Sub Menu 2-5</a></li>
        <li><a href="#">Sub Menu 2-6</a></li>
        <li><a href="#">Sub Menu 2-7</a></li>
      </ul>
    </li>
    <li>
      <a href="#">Main 3 this one goes for a while and does not wrap</a>
      <ul>
        <li><a href="#">Sub Menu 3-1</a></li>
        <li><a href="#">Sub Menu 3-2</a></li>
        <li><a href="#">Sub Menu 3-3</a></li>
        <li><a href="#">Sub Menu 3-4</a></li>
      </ul>
    </li>
    <li>
      <a href="#">Main 4</a>
      <ul>
        <li><a href="#">Sub Menu 4-1</a></li>
        <li><a href="#">Sub Menu 4-2</a></li>
        <li><a href="#">Sub Menu 4-3</a></li>
        <li><a href="#">Sub Menu 4-4</a></li>
      </ul>
    </li>
  </ul>
</menu>

A menu tag to provide semantic context, with a top-level unordered list to provide structure for the top level of the menu, and nested unordered lists to provide structure for the drop-down (or sub) menus.

Now, let’s do the layout with some basic CSS.

menu ul {
  padding: 0;
  margin: 0;
  list-style-type: none;
}

#mainmenu {
  width: 55em;
}
#mainmenu > ul > li {
  float: left;
  width: 25%;
}

#mainmenu a {
  display: block;
  padding: 0.5em;
  background-color: black;
  text-decoration: none;
  color: white;
  font-family: arial;
  text-overflow: ellipsis;
  overflow: hidden;
  white-space: nowrap;
}

Here we eliminate the default margins and padding, and bullets, for the unordered lists that make up our menu.  Then we float the list items for the top level menu to the left.  We also set a width for the menu, and divide this width equally (25%) among the four sub-menus. At this point we should have the items lining up correctly, with top level items going from left to right, and their corresponding sub-menu items rendering beneath.

We’re also going to do our aesthetic styling on the hyperlinks, as this will allow for the link/visited/hover/active behavior to take up the entire space of the list items in he menu.  To do so, simply specify the hyperlink (“a”) elements to be display blocks, and set any padding/margin on them.  We also set the default background color, font face and text color here, as well as setting the text-overflow to ellipsis in case the text is longer than our width.

We can now hide the sub-menus and get ourselves into our default state simply by adding this CSS:


#mainmenu > ul > li > ul {
  height: 0;
  overflow: hidden;
}

We just set the height of our sub-menus (he nested unordered lists) to 0, and hide any overflow so it’s not visible.  (We could use “display:none” here and also have it function, but this won’t work in conjunction with the animated effect we will add shortly).

So, to get our menu functional so the corresponding sub-menu displays when you hover over a main menu item, it’s simply a matter of setting the height to it’s appropriate height:


#top > ul > li:hover > ul {
  height: auto;
}

#mainmenu > ul > li:hover a {
  background-color: dimgray;
}

#mainmenu ul li a:hover {
  background-color: gray;
}

We also set the background color to change to indicate the hover over menu item and sub-menu item states.  At this point we have a fully functional drop down menu that works on browsers back to IE7.  And with no JavaScript!

Finally, we can animate the “dropping down” of the menu with a simple CSS3 transition.  We are going to do this on the “max-height” property of the nested unordered lists by setting them to have a max-height in the hover state that is greater than any possible height for our sub-menus.  In the default/non-hover state it will have a max-height of 0.  And we’ll set a transition for the max-height property to create the animated effect:


#mainmenu > ul > li > ul {
  height: 0;
  overflow: hidden;
  max-height: 0; /*start state of animation/transition*/
  transition: max-height 0.5s ease-in;
}

#mainmenu > ul > li:hover > ul {
  height: auto;
  max-height: 10em; /*end state of animation/transition*/
}

So the question may come up as to why we didn’t simply apply the transition to the sub-menus’ height attribute.  The quick answer is it doesn’t work, at least not if we set the height to “auto”.  Even if it were to work, and you may be able to get it to work if you set the heights of the menu items to a specific value, it may still not provide the ideal behavior.  By animating to the same “max-height” for each sub-menu, you ensure they sub-menus move at the same speed regardless of height differences.

And why not remove height altogether since we’re relying on max-height for the animation? Just a matter of preference for how the menu works when you mouse off the main menu item.  If we remove height, then you also see the menu animating to a closed/hidden state, which to me looks awkward, especially when another sub-menu is animating open at the same time.  Try it, and you may see what I mean.

And that’s it, an animated drop down menu with no JavaScript.  What I haven’t done is test this on mobile devices, though for that we may want to restructure the menu with a media query.  A possible future addendum for this article.

Questions?  Thoughts?  Feedback?