CSS3 animated dropdown menu

It’s a sure thing that CSS3 features like transitions, animations and transforms can add extra spice to your designs.

In this article you will see how you can build an awesome CSS3 animated dropdown menu with some of these cool features.

CSS3 animated dropdown menu

View demo

Here’s a quick preview for the CSS3 animated dropdown menu that we’re going to create today:

Remember the previous CSS3 dropdown menu? That menu is awesome, and thanks to you is the most popular tutorial around here (at this time).

Perhaps the best title for this article would have been: CSS3 dropdown menu – Revisited. The reason I’m saying that is because in this article you’ll learn how to create an animated CSS3 dropdown menu based on our previous example.

The HTML

The HTML structure hasn’t changed at all, simple and minimal. Here’s an excerpt:

<ul id="menu">
	<li><a href="#">Home</a></li>
	<li>
		<a href="#">Categories</a>
		<ul>
			<li><a href="#">CSS</a></li>
			<li><a href="#">Graphic design</a></li>
			<li><a href="#">Development tools</a></li>
			<li><a href="#">Web design</a></li>
		</ul>
	</li>
	<li><a href="#">Work</a></li>
	<li><a href="#">About</a></li>
	<li><a href="#">Contact</a></li>
</ul>

The CSS

I revised and improved the styles in order to create this unique CSS3 animated dropdown menu. So, below you can find the commented pieces of styles:

Mini reset

Reset the default ul styles.

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

Main level

The #menu is basically the main ul for this menu. CSS3 things like gradients, shadows and rounded corners help us to create the below:

CSS3 menu wrapper

#menu {
	width: 960px;
	margin: 60px auto;
	border: 1px solid #222;
	background-color: #111;
	background-image: -moz-linear-gradient(#444, #111);
	background-image: -webkit-gradient(linear, left top, left bottom, from(#444), to(#111));
	background-image: -webkit-linear-gradient(#444, #111);
	background-image: -o-linear-gradient(#444, #111);
	background-image: -ms-linear-gradient(#444, #111);
	background-image: linear-gradient(#444, #111);
	-moz-border-radius: 6px;
	-webkit-border-radius: 6px;
	border-radius: 6px;
	-moz-box-shadow: 0 1px 1px #777;
	-webkit-box-shadow: 0 1px 1px #777;
	box-shadow: 0 1px 1px #777;
}

Clear floats

Here is Nicolas Gallagher‘s clearing method I’ve been using lately:

#menu:before,
#menu:after {
	content: "";
	display: table;
}

#menu:after {
	clear: both;
}

#menu {
	zoom:1;
}

List elements

CSS3 menu elements styles

Please notice the #menu li:hover > a selector. This is perhaps the most important CSS trick for this CSS3 dropdown menu.

So, this is how this works: Select an “a” element that is child of a “li” ; the “li” element must be a descendant of the “#menu”. Read more here.

#menu li {
	float: left;
	border-right: 1px solid #222;
	-moz-box-shadow: 1px 0 0 #444;
	-webkit-box-shadow: 1px 0 0 #444;
	box-shadow: 1px 0 0 #444;
	position: relative;
}

#menu a {
	float: left;
	padding: 12px 30px;
	color: #999;
	text-transform: uppercase;
	font: bold 12px Arial, Helvetica;
	text-decoration: none;
	text-shadow: 0 1px 0 #000;
}

#menu li:hover > a {
	color: #fafafa;
}

*html #menu li a:hover { /* IE6 only */
	color: #fafafa;
}

Submenus

With CSS3 transitons we can animate changes to CSS properties like margin or opacity. This is very cool and I’ve used this for animating the CSS3 sub-menus. The result is great if you ask me:

#menu ul {
	margin: 20px 0 0 0;
	_margin: 0; /*IE6 only*/
	opacity: 0;
	visibility: hidden;
	position: absolute;
	top: 38px;
	left: 0;
	z-index: 9999;
	background: #444;
	background: -moz-linear-gradient(#444, #111);
	background: -webkit-gradient(linear,left bottom,left top,color-stop(0, #111),color-stop(1, #444));
	background: -webkit-linear-gradient(#444, #111);
	background: -o-linear-gradient(#444, #111);
	background: -ms-linear-gradient(#444, #111);
	background: linear-gradient(#444, #111);
	-moz-box-shadow: 0 -1px rgba(255,255,255,.3);
	-webkit-box-shadow: 0 -1px 0 rgba(255,255,255,.3);
	box-shadow: 0 -1px 0 rgba(255,255,255,.3);
	-moz-border-radius: 3px;
	-webkit-border-radius: 3px;
	border-radius: 3px;
	-webkit-transition: all .2s ease-in-out;
	-moz-transition: all .2s ease-in-out;
	-ms-transition: all .2s ease-in-out;
	-o-transition: all .2s ease-in-out;
	transition: all .2s ease-in-out;
}

#menu li:hover > ul {
	opacity: 1;
	visibility: visible;
	margin: 0;
}

#menu ul ul {
	top: 0;
	left: 150px;
	margin: 0 0 0 20px;
	_margin: 0; /*IE6 only*/
	-moz-box-shadow: -1px 0 0 rgba(255,255,255,.3);
	-webkit-box-shadow: -1px 0 0 rgba(255,255,255,.3);
	box-shadow: -1px 0 0 rgba(255,255,255,.3);
}

#menu ul li {
	float: none;
	display: block;
	border: 0;
	_line-height: 0; /*IE6 only*/
	-moz-box-shadow: 0 1px 0 #111, 0 2px 0 #666;
	-webkit-box-shadow: 0 1px 0 #111, 0 2px 0 #666;
	box-shadow: 0 1px 0 #111, 0 2px 0 #666;
}

#menu ul li:last-child {
	-moz-box-shadow: none;
	-webkit-box-shadow: none;
	box-shadow: none;
}

#menu ul a {
	padding: 10px;
	width: 130px;
	_height: 10px; /*IE6 only*/
	display: block;
	white-space: nowrap;
	float: none;
	text-transform: none;
}

#menu ul a:hover {
	background-color: #0186ba;
	background-image: -moz-linear-gradient(#04acec,  #0186ba);
	background-image: -webkit-gradient(linear, left top, left bottom, from(#04acec), to(#0186ba));
	background-image: -webkit-linear-gradient(#04acec, #0186ba);
	background-image: -o-linear-gradient(#04acec, #0186ba);
	background-image: -ms-linear-gradient(#04acec, #0186ba);
	background-image: linear-gradient(#04acec, #0186ba);
}

First and last list elements styles

#menu ul li:first-child > a {
	-moz-border-radius: 3px 3px 0 0;
	-webkit-border-radius: 3px 3px 0 0;
	border-radius: 3px 3px 0 0;
}

#menu ul li:first-child > a:after {
	content: '';
	position: absolute;
	left: 40px;
	top: -6px;
	border-left: 6px solid transparent;
	border-right: 6px solid transparent;
	border-bottom: 6px solid #444;
}

#menu ul ul li:first-child a:after {
	left: -6px;
	top: 50%;
	margin-top: -6px;
	border-left: 0;
	border-bottom: 6px solid transparent;
	border-top: 6px solid transparent;
	border-right: 6px solid #3b3b3b;
}

#menu ul li:first-child a:hover:after {
	border-bottom-color: #04acec;
}

#menu ul ul li:first-child a:hover:after {
	border-right-color: #0299d3;
	border-bottom-color: transparent;
}

#menu ul li:last-child > a {
	-moz-border-radius: 0 0 3px 3px;
	-webkit-border-radius: 0 0 3px 3px;
	border-radius: 0 0 3px 3px;
}

The jQuery

As you already get used to, IE6 gets some extra attention:

$(function() {
  if ($.browser.msie && $.browser.version.substr(0,1)<7)
  {
	$('li').has('ul').mouseover(function(){
		$(this).children('ul').css('visibility','visible');
		}).mouseout(function(){
		$(this).children('ul').css('visibility','hidden');
		})
  }
});

While the :hover pseudo-class does not work for other elements than anchor, we just need to add this small jQuery snippet to fix it. It's pretty self-explanatory.

View demo

Your turn

I hope you enjoyed this article and the techniques I used. Please share your comments and questions below!

Related content

Written by Red

Catalin Rosu, a.k.a Red, is a professional web designer and developer who loves to be creative and enjoys CSS techniques. Stay tuned for latest updates, subscribe to RSS and follow him on Twitter.

60 Responses to “CSS3 animated dropdown menu”

  1. Yuneekguy says:

    You know, you are some CSS genius! Everytime I see a yellow glow (indicating a new item on my feed) from red team design feed on my Google reader, I leave everything else to check it. You always make us look forward to something really cool. I’d be working on a project soon. If i stumble on any piece that seems to gimme issues, I sure know where to call. Thanks for sharing as always.

  2. Aaron Stacy says:

    thanks for the great post, red! i think the menu’s look great and and you’ve done a good job explaining it. the only thing i’m curious about: how are you placing the triangles around the first li element?

    i notice that they go away when i comment out the ‘#menu ul li:first-child > a:after’ rule in the css, but i can’t tell exactly how they’re being created.

    thanks again!

  3. Peter says:

    This menu looks great! I really hope IE9 and IE10 will spread fast. Right now around 7% of our users still use IE8 or something older, but we’re in a design oriented business and on an average site this number can be more than 15%. The menu works well in IE8 but of course it’s missing all the little details that makes it stand out so all we can hope is that these outdated browsers die out soon… :)

    • Red says:

      Hi Peter, thanks for your comment .

      Of course, the “little details that makes it stand out” are missing for older browsers. But, I think the users who run on older browsers won’t feel the difference.

      The most important thing is that, while being wonderful on modern browsers, this menu works perfect also on older browsers like IE trident.

  4. Yoan says:

    Hi, can you try putting :focus so it can be used using the keyboard too?

    • Stomme poes says:

      You can try this to at least show the whole submenu (first level anyway) on focus of the top-level anchor:

      #menu li:hover>ul, #menu a:focus+ul {
      opacity: 1;
      visibility: visible;
      margin: 0;
      }

      Unfortunately because opacity and visibility has been set on the ul and not the anchors, it’s not possible to undo the opacity of a parent while focussing on the child. For this reason I’m still a fan of the pull-offscreen negative margin technique, because you can also change the margins on the child anchors as well.

      But then this fade-in CSS3 technique won’t work. A way around this would probably be to leave pos:absolute on the sub uls but move the opacity and visibility settings to the anchors, so you can remove them on :focus.

      With this, you can get the child anchors to appear on focus (you won’t see their submenu siblings, but at least you’ll see the item itself).
      There’s an example of this menu (disable javascript to see just the CSS) at www motorrijschoolemo.nl tab through the menu. Turning Javascript back on, you see it assists in keeping the whole sub-ul onscreen as well (and helps IE6).

      You’d still need a touch of Javascript to keep the whole sub-ul to stay onscreen.

  5. Nathan says:

    Very cool menu. I was just wondering your thoughts on keyboard accessibility. As I tab through the demo I can’t find a way to get the drop down menus to come up. Maybe capturing the enter or arrow keys to expand, but I get that requires more JS. Maybe add :focus to everything with :hover?

  6. Red says:

    Yoan and Nathan, the :focus pseudo-class is not a solution in order to make this menu accessible via keyboard. I’m pretty sure that some JavaScript would be required to do that.

    I will think about a solution regarding this matter.

    Thanks for your suggestions. ;)

  7. ptamaro says:

    I would think that access keys would provide keyboard accessibility in this situation — at least for the main links, right? For example…

    Home

    Just a thought. Great post, and the menu is really tasty… nice job :)

    • ptamaro says:

      <a href=”index.html” accesskey=”h”>Home</a>

      • Stomme poes says:

        So long as you find an accesskey that doesn’t conflict with a taken browser or other software key (why numbers are recommended, and even then you’ll hit some software), and then find a way to make it known to the user that the accesskeys exist (only Opera offers a method for all users to discover accesskeys).

  8. amidude says:

    Very nice tutorial. Thank you so much for sharing. I’m just curious what purpose the empty “content:” properties in your code serve.

    • amidude says:

      I found the explanation. The pseudo-element won’t work without the content property being declared…even if it’s empty. Great article…thanks again.

      • Red says:

        amidude, indeed, you need to specify the content for a pseudo-element, even if is empty.

        Thanks for your comment, I’m glad you like this article.

  9. Andrew Groat says:

    This is awesome! Red your content keeps getting better and better!

    Great work once again :-)

  10. Chiddy says:

    bro, what can I say
    most coding blogs are too simple or too complicated but I acctually understand what you mean. Cheers cuzz

  11. Ingo says:

    Looks nice but doesn’t work on IE9/ Win7. The submenu disappears when I try to reach it.

  12. Brilliant! Often these tutorials are not cross-browser friendly. This was perfect! Love the effort you put in your tutorials. I am now bookmarking them!

    • Red says:

      Thanks John, I really appreciate it!

      Regarding cross-browser compatibility, I always struggle to do that (at least I try my best). :)

  13. Red, what do offer as a solution to a top-level menu item being positioned near the right of the viewport, and its contents being more than 2 levels deep? i.e. the submenus will display outside of the viewport.

    • Red says:

      Shaun, I’d say that is not a common behavior.

      At first sight, if I’d really want a menu right-aligned, I’d add just one level to it and then reorganize the other possible sub levels into a sidebar for example.

      I’m totally against complexity when it comes about menus, and to avoid eventually JavaScript viewport calculations, I’d choose the solution above.

    • Stomme poes says:

      What many people do is give the last main menu item a class like “last” or whatever, and instead of having the child menus sit to the left, they’re positioned to sit to the right. A right-flyout.

      However Red’s point is good: deeply-nested menus are a problem for plenty of people, and you probably want to organise your menu for best usability/accessibility to have really just one level submenu, not multiple levels. The deeper people need to move, esp with mice, the more likely they are to lose their :hover and inadvertently close their menu. Which gets frustrating.

      So try reorganisation first, but the “left” class technique is not uncommon.

      • Stomme poes says:

        Sigh, my dys-spatia showing again.

        Re-do:
        Instead of letting the child submenus sit to the *right*, they are positioned to the *left*. So they don’t go past the viewport width.

  14. Stoo says:

    This is pretty funky, great work.

    Now all I need is to make this in to a sidebar that slides out to the right :D

  15. chandra sekhar says:

    its a great article thank you

  16. Steven says:

    You may want to reconsider the check for IE6: “$.browser.version.substr(0,1)<7" is evil. It will break with IE10, which starts with a 1.

  17. Stomme poes says:

    I’m bookmarking this, because I’m being asked more and more to add a bit of animation to menus and I’ve been resisting Javascript.

    Two nitpicks:
    Setting the z-index on the #menu ul may still hurt anyone on IE6-7 where a relatively positioned element comes after the menu (the IE z-index bug, as explained at annevankesteren.nl/2005/06/z-index)
    so I’d put it on the menu itself, or the top-level li’s if you don’t want to position the menu. Z-index on the parent hits the subs too, but also overrides the next positioned element coming after the menu.

    Second: The zoom statement isn’t needed. You’re already hitting Haslayout with a width on this #menu so you’re already good.

    Keyboard accessibility is another point but I’ve already mentioned that in an earlier comment.

    Cheers

    • Red says:

      Stomme,

      regarding the z-index, the #menu ul is already absolute positioned and that’s why defining a z-index is recommended.

      Also, the zoom is added just for clearing floats purposes.

      Thanks for sharing your thoughts with us.

      • Stomme poes says:

        Hi Red,
        If you’re only adding a z-index because you’ve positioned an element, you can leave it out, since abso-po’d elements are naturally stacked higher than their rel-po’d or static siblings and ancestors.
        The reason people put z-index on there at all is because they worry about someone who might be coming after the menu, and want to be certain the submenus always stack above (since the elements after the menu are later in source, and might be positioned, this is a valid fear).

        So setting a good z-index on the submenus is usually a good idea, and it works on all other browsers.
        However IE6 and 7 can bite you in the butt, because of this known bug.
        If you know you won’t have any positioned elements coming after the menu anyway, you don’t have to worry.
        But try it: have a #menu, then after the menu have a #main page element, and set it to position: relative for kicks. Then see what IE6-7 do. It’s a very common complaint/problem on web development forums: my submenu is fine in all browsers except in IE it’s behind my page/Flash/whatever. (though Flash is a different issue).

        Re zoom: it doens’t contain floats at all. It never has.

        IE 6 and 7 have Haslayout though. One of the strange and unique properties of Haslayout is, a container who has “Layout” in the Trident engine will always *contain* floated children (instead of letting the floats hang out like the specs say it should, and the way all other browsers do it).
        www satzansatz.de/cssd/onhavinglayout.html

        Zoom is one property that gives someone “Layout”, so you can make a box contain its child-floats in IE6 and 7 by setting zoom:1 (or zoom: anything but of course we don’t want any zooming) on it. All other browsers (including IE8+) will ignore zoom.

        But, if you trigger Haslayout some other way, then zoom can be thrown out. You’ve set a width on this menu, and width is also a trigger. Lucky us. We get “Layout” on our #menu for free! Which is awesome here because we’ve got floated children.

        The :after stuff technically doesn’t clear anything either because this technique *contains* the floats (tables are required to enclose their children no matter what… including fake display-tables : )

        I admit setting display:table on the generated element is much less code than Tony Aslett’s “create an element and use it to clear and then try to make it invisible” clearfix. The :before section I recognise from Yahoo, though maybe that guy works for Yahoo. There was some weird edge case where also adding a :before element fixed something and it’s not much extra code.

        Still, they are nitpicks and not huge amounts of code, and the menu is still brilliant.

        • Stomme poes says:

          Sigh, even though I re-read before hitting post…

          Should have been:
          “Re zoom: it doens’t clear floats at all. It never has.”

          “Contain” is exactly what it does, for IE6-7. My bad.

        • @Stomme: “The :after stuff technically doesn’t clear anything either”.

          Yes, it does. Because that technique contains floats only in IE6/7. In other browsers, it does clear (hence why clearfix sucks [1] most of the time).

          Regarding the use of “:before”, it is to prevent collapsing margin (on first child) across the board [2].

          Like you, I noticed the use of a width *and* zoom on the menu, but this is because zoom comes from clearfix. It is part of the clearing method, it does not come from styling the layout.

          @Red: Stomme is right about z-index. But best practice is to create a stacking context at the lowest level – which is the menu. Also, I think authors should never ever use z-index values greater than 3 or 4 in tutorials as it makes people think that using huge number like this is the way to go; that it will prevent “something” from happening when in fact it will create more issues than it solves.

          To Stomme point, if you position the menu and give that element a z-index to create a stacking context, you’ll “beat” any positioned element coming after that menu in any browser (including IE6).

          You can check this page [3]. It lets you create different stacking contexts to see how z-index works.

          Anyway, you have created a very nice menu here. Let’s hope people will see this as a nice ‘exercise’ rather than something they *have to use*. Because imho these menus create usability issues for many users.

          (sorry for the plugs) :-(

          [1] http://thinkvitamin.com/design/everything-you-know-about-clearfix-is-wrong/
          [2] http://www.yuiblog.com/blog/2010/09/27/clearfix-reloaded-overflowhidden-demystified/
          [3] http://www.tjkdesign.com/articles/z-index/teach_yourself_how_elements_stack.asp

          • Red says:

            Thierry Koblentz, wow! I’m glad you like this menu and thanks for your comment, I always appreciate your thoughts.

            I must admit that, usually, I choose to use higher values for z-index property thinking that this way I could prevent “bad things” happening when user will implement it in their projects.

  18. Johnny says:

    Great menu. Thanks a lot!

    Have a couple of questions:

    If I don’t want the rounded corners. Which css to take out. Tried taking out the moz-borders, but that didn’t do the trick.

    Also, I would like to have one menu item (with a language selection), far out right. Can you make a space in-between the last real menu item and a far right one?

    • Red says:

      Johnny, thanks for your comment.

      If you don’t want rounded corners, just remove all the following declarations:

      -moz-border-radius: ...;
      -webkit-border-radius: ...;
       border-radius: ...;
      

      Regarding your second question, you need to set float:right for the last #menu li.

      • Johnny says:

        Wow, very quick reply. I’ll give it a shot. Thanks!

        • Johnny says:

          Hi again,

          Ok, the corners gone. Just can’t see how I’ll get that last li moved to the right. How do you set float:right for the last menu li, when all others are left

          • Red says:

            In HTML, you must add a last-menu-item ID to the last li just like that:

            <li id="last-menu-item"><a href="">Select language</a></li>
            

            Then, in the CSS file:

            #last-menu-item {
            float: right;
            }
            

            Hope that helps!

          • Johnny says:

            eventually did:

            Html: “Select language

            Css: #menu li.last-menu-item {
            float: right;
            }

            that worked!

            Thanks again!

  19. Abetam says:

    great stuffs…

    I wonder if you could come with a tutorial to produce a menu tree similar to http://www.treemenu.net/

    :D

    Cheers

  20. Jason says:

    First, I just wanted to say thank you for such an awesome menu! I’ve just started integrating it into my site and it’s beautiful!

    One question I had – on iPhone/iPad users can’t hover so I was wondering if there is a way to make the sub-menu appear on click (tap) of the main menu items?

    Thank you!

    • Red says:

      Thanks for your comment, Jason. I’m glad you like it!

      Regarding your question, indeed, with adding some JavaScript, you could use the click event instead the hover for this menu.

      Though, I wouldn’t recommend you that. Instead, you could try some CSS3 media queries to transform your menu into an accessible one.

      • Jason says:

        Thank you so much for the reply! That helps!

        One other question that I’m not sure if you can help with. I have some Fusion Charts on my page and have to include the FusionCharts.js file (). However, when I do that, the menu stops working only in Safari – still works fine in other browsers. When I hover over a main menu item, the pull down menu doesn’t appear. Again, this only happens in Safari and as soon as I take out the script above, it works normally again.

        Is there something specific I should look for in the FusionCharts.js script that may help to make this issue go away?

        Thank you!
        Jason

        • Red says:

          Jason, I’m afraid I can’t help you with this. Anyway, it’s a little strange you have this problem only on Safari.

          All I can recommend you is to check your JavaScript for errors. If you’re using Firebug on Windows, then try: CTRL+SHIFT+J

          Good luck!

  21. oli says:

    Hi,
    thanks for this awesome menu. :)
    how can i remove the black bar? let me know please.
    waiting for your reply..
    thanks
    oli

  22. Leo says:

    Hi, Red. Thanks for an awesome work. I’m upgrading my site to fit your menu but i have a problem with submenues. Lot’s of li elements have somereallylongnames and some left over text just doesn’t fit the box and goes over. Is there a way to adjust the width of all elements to be as wide as needed to fit the text inside?
    Thanks in advance!

    • Red says:

      Hi Leo, the thing is that you can’t make this dropdown menu fluid without JavaScript. So, the best way is to choose a width based on your longest menu item. Hope that helps!

      • Leo says:

        Hi!
        “white-space: nowrap” removal helped (so, long items go as 2 lines) and yes, this way fixed width of a colums for all child elements is the best way to go without Javascript. And still, navigation looks awesome! Thanks for your post!!!

Leave a Reply