Dropdown Menu Transitiony AWESOMENESS


I have struggled a great many times to figure out how to use all of the nifty transitions in my CSS dropdown menus. Some, like fades, are easy. Others, however, require a sacrificial chicken and a prayer to the CSS gods that it’ll work. This post will give a very basic tutorial on building pure CSS menus, then a few cool animations that I’ve figured out.

Disclaimer: At the time of this writing transitions still require browser prefixes to work for Firefox (-moz-), Opera (-o-), and Webkit (-webkit-). I have omitted them from the code examples here for readability but they will be required if you intend to use these techniques.

The Menu

First, we’re going to build a super basic menu:

<ul class="nav">
  <li>
    <a href="index.html">Home</a>
  </li>
  <li>
    <a href="about.html">About</a>
    <ul>
      <li>
        <a href="about.html#company">Company</a>
      </li>
      <li>
        <a href="about.html#team">Team</a>
      </li>
    </ul>
  </li>
  <li>
    <a href="contact.html">Contact</a>
  </li>
</ul>

Now let’s give it some styling:

ul {
  background:#99f;
  border-radius:10px;
  list-style:none;
  margin:0;
  overflow:hidden;
  padding:0;
}

.nav > li {
  display:block;
  float:left;
}

.nav li a {
  color:#fff;
  display:block;
  padding:20px;
  text-decoration:none;
}

.nav li ul {
  background-color:#bbf;
  border-radius:0 0 10px 10px;
  display:none;
  position:absolute;
}

.nav li:hover {
  background-color:#bbf;
}

.nav li:hover ul {
  display:block;
}

.nav li li {
  white-space:nowrap;
}

View Demo

Let’s break it down by rules:

ul

We take all of the styling off of the ul which would normally be done using a reset like Eric Meyer’s Reset or Normalize.css. I also gave it overflow:hidden; just to clear the floats. Normally you would use your clearfix for that. The border-radius is just there because it’s cool.

.nav > li

Next we float our list items to get a horizontal menu (these same techniques can work for a vertical menu, as well). We used the child selector to make sure this styling only hits our top level items.

.nav li a

We’re going to use the anchor tags inside of our list items for styling. Otherwise backgrounds, border-radius, width, etc. can get muddled up when we show our submenu. Most of this is just styling, though.

.nav li ul

Now we’ll hide our submenus and give them some styling. We also position them absolutely so they appear outside of our top level items when we finally display them.

.nav li:hover

More styling. I’m changing the background color for both the top level menu item and the submenu for a better visual cue.

.nav li:hover ul

This will show the submenu when we hover over the parent container.

.nav li li

More styling. I use the white-space:nowrap; to make sure the options always stretch out instead of wrapping to multiple lines.

Getting Fancy

Alright, let’s add some pretty to this menu!

Slide In/Out

All we have to do here is transition the height of the submenus. Unfortunately, using the submenu’s display mode to show and hide it breaks our transition since display isn’t on the list of animatable properties. That’s fine, though, since the menu will have a height of 0, effectively hiding until its parent is hovered. I’ll use max-height instead of height to pull it off so I don’t need to give my menus a static height. One thing to keep in mind is that using max-height means that the higher your max-height value, the faster your menus will animate.

ul {
  background:#99f;
  border-radius:10px;
  list-style:none;
  margin:0;
  overflow:hidden;
  padding:0;
}

.nav > li {
  display:block;
  float:left;
}

.nav li a {
  color:#fff;
  display:block;
  padding:20px;
  text-decoration:none;
}

.nav li ul {
  background-color:#bbf;
  border-radius:0 0 10px 10px;
  max-height:0;
  position:absolute;
  transition:max-height .5s;
}

.nav li:hover {
  background-color:#bbf;
}

.nav li:hover ul {
  max-height:500px;
}

.nav li li {
  white-space:nowrap;
}

View Demo

Fade In/Out

We’ll simply use a transition and the submenu’s opacity to fade in and out. However, we will have to borrow the max-height transition from the slide effect:

.nav > li {
  background-color:transparent;
  display:block;
  float:left;
  transition:background-color .2s;
}

.nav li ul {
  background-color:#bbf;
  border-radius:0 0 10px 10px;
  max-height:0;
  opacity:0;
  position:absolute;
  transition:max-height 0 .4s, opacity .4s;
}

.nav li:hover {
  background-color:#bbf;
  transition:background-color .2s;
}

.nav li:hover ul {
  max-height:1000px;
  opacity:1;
  transition:opacity .4s;
}

View Demo

There are a couple of things to mind with this transition. The max-height transition is necessary to make sure the submenu fades in and out. We use a transition-duration of 0 with no transition-delay for the max-height to ensure the menu is hidden (height of 0) until it is hovered, then we delay the max-height transition until after the opacity transition to ensure it doesn’t immediately disappear. If you test the demo without the max-height, you’ll see what I mean. For a better understanding check out my previous post about transitions.

Sliding Panel?

Yeah, I dunno what to call it so “Sliding Panel” was the best I could come up with. Anywho, this once again uses max-height but we add in max-width with a delay to make a vertical bar come down, then the menu opens horizontally. Just swap the delay values for the properties if you want to use this effect on a vertical menu:

.nav > li {
  display:block;
  float:left;
  transition:background-color .2s .8s;
}

.nav li ul {
  background-color:#bbf;
  border-radius:0 0 10px 10px;
  max-height:0;
  max-width:4px;
  position:absolute;
  transition:max-height .4s .4s, max-width .4s;
}

.nav li:hover {
  background-color:#bbf;
  transition:background-color .2s;
}

.nav li:hover ul {
  max-height:500px;
  max-width:500px;
  transition:max-height .4s .2s, max-width .4s .6s;
}

View Demo

This effect gets a bit more complicated than the previous examples. We actually used the transition-delay 0 to allow the parent link to change its background-color first, then we slide the submenu down as a thin vertical bar. We set the max-width on the initial state to 4px instead of 0 so that you can see the bar when it comes down. We then open up the menu horizontally to reveal all of the links.

Flippity-Floppity

One caveat – this one will only work in Webkit browsers right now. Eventually everyone else will catch up on CSS3 3D transforms but we’re not there yet. We’ll use 3D transforms to make the menu flip down 3D style when you hover over its parent. Check it:

ul {
  background-color:#99f;
  border-radius:10px;
  list-style:none;
  margin:0;
  padding:0;
}

ul:after {
  clear:both;
  content:'';
  display:block;
  height:0;
  margin:0;
  padding:0;
  width:100%;
}

.nav > li {
  display:block;
  float:left;
  perspective:600px;
  perspective-origin:100% 100%;
  transition:background-color .2s .4s;
}

.nav li ul {
  background-color:#bbf;
  border-radius:0 0 10px 10px;
  position:absolute;
  transform:rotateX(-90deg);
  transform-origin:0 0 0;
  transition:transform .4s;
}

.nav li:hover {
  background-color:#bbf;
  transition:background-color .2s;
}

.nav li:hover ul {
  transform:rotateX(0);
  transition:transform .4s .2s;
}

View Demo

This one has a lot more going on so let’s break it down rule by rule again:

ul

I removed overflow:hidden; because it can cause your absolutely positioned 3D objects to clip.

ul:after

Just a normal clearfix. You should have one of these if you’re using a floated layout, so just add your clearfix class to the ul and you’ll be good to go. I didn’t have to add it until now because we were previously able to use overflow:hidden;, another way to clearfix elements.

.nav > li

We’re going to introduce a new property called perspective:x y;. This allows us to tell the browser how far away from the user this element’s children should pretend to be. I know, brain-breaking. I may write more on this later but for now check out this article for all the deets. We also use perspective-origin to set the perspective focus to the bottom-right of the parent element. Setting the x origin to the right side isn’t necessary but I like the extra angle it gives to the fall of the submenu. Setting the y origin to the bottom, however, is necessary to get the submenu to lay flat when it’s flipped up. Otherwise it can cover up part of the main menu.

.nav li ul

Here we use transform:rotateX(); to flip the submenu 90° backward (behind the menu). Next we zero out transform-origin:x y z; so that our anchor point is at the top left of the submenu. Setting the z origin to 0 is unnecessary since it defaults to 0 (the x and y default to 50%), I just set it for reference when I’m doing 3D transforms.

.nav li:hover

No change here.

.nav li:hover ul

Finally we’ll let the submenu drop from behind the parent menu and voila! We’re using the transform property to set the menu at a -90° rotation on the X axis. We’re also using the transform-origin property to set the X axis to the top of the menu. Now when you roll over the parent, the menu (which is attached like a hinge to the bottom of the parent) will flip down from behind it and towards the screen.

Conclusion

These are all pretty basic as far as awesome transitions go, but when combined with the rest of your styling arsenal they can create some really stunning effects. I’ll try to do another post eventually with some more advanced transitions. In the meantime, leave a comment if you have any other cool transitions!

No Comments

Leave a response