Horizontal CSS Dropdown Menus

Published May 20, 2008 by Rob Glazebrook.

Last week, CSS Newbie reader Andrea Pluhar wrote in with an interesting problem: she wanted to use CSS dropdown menus like the ones we featured last week on a website that she was building, but the design called for the submenu to be arranged horizontally, not vertically. She sent me a mockup of what she was after (excerpted above) and wondered if there was a way to accomplish this effect using CSS. It turns out that there is a CSS-riffic way to do this, and in the spirit of maximizing benefit, I thought a tutorial would be in order.

The XHTML involved is identical to that used in our regular dropdown menus: a nested unordered list, where the nested lists become the submenus. It looks something like this:

<ul id="navbar">
	<li><a href="#">Item One</a><ul>
		<li><a href="#">Subitem One</a></li>
		<li><a href="#">Second Subitem</a></li>
		<li><a href="#">Numero Tres</a></li></ul>
	</li>
	<!-- ... and so on ... -->
</ul>

Next we’ll move to the CSS. I started out by moving the navigation bar to the top-right corner, like the design called for, removing the list styling, and floating the items left to make them line up in a row:

#navbar {
	position: absolute;
	top: 0;
	right: 0;
	margin: 0;
	padding: 0;}
#navbar li {
	list-style: none;
	float: left; }

Next, I styled the primary anchor tags to make them look more like the navigation Andrea was looking for. The code looks like this:

#navbar li a {
	display: block;
	padding: 3px 8px;
	text-transform: uppercase;
	text-decoration: none; 
	color: #999;
	font-weight: bold; }
#navbar li a:hover {
	color: #000; }

I’ve added a bit of padding to the link, and used the text-transform property to make everything uppercase like the mockup called for. That way, the original XHTML can be lowercase or camel-case (capitalized first letters)… which would be a little easier to read in an unstyled document.

Next up, we hide the nested lists by default, and then style them when the containing list item is hovered over:

#navbar li ul {
	display: none;  }
#navbar li:hover ul, #navbar li.hover ul {
	position: absolute;
	display: inline;
	left: 0;
	width: 100%;
	margin: 0;
	padding: 0; }

The code above is the bit that really makes most of this magic work, so I’ll explain the important parts in some detail. First, because IE6 doesn’t support hover states on anything other than anchor tags, we’re writing our rules to account for the hover state and a hover class. This class is applied to elements when they’re being hovered over, using an ingenious little bit of JavaScript (which is explained in this previous dropdown menu tutorial).

Next up, we’re absolutely positioning our nested lists and using the “left” property to move the list to the left-most side. This isn’t moving the list to the left-most side of the screen, but instead the left-most side of its parent positioned element, which in this case happens to be the main unordered list that we positioned right at the start. As such, this trick relies of the whole list being positioned in some manner, even if it’s just relatively positioned and left in place.

The display: inline rule is a little more complicated. So much so, I don’t even completely understand what it’s doing. What I do know is, without that rule, the list items in the submenus simply don’t show up whatsoever in any major browser. I think it has something to do with the fact that the containing elements are floated (which we’ll get to in a bit), but I can’t prove that. If anyone has any better insight into the technical aspect, please let me know in the comments.

Lastly, the width: 100% rule is somewhat important. It’s preventing the unordered list from collapsing down to a smaller size in certain browsers. Specifically, without setting this width specified, the nested list sometimes collapses to the size of its “containing” list item (even though it’s absolutely positioned and therefore technically no longer contained). Note that older versions of Opera don’t deal well with the 100% width… if you want it to work on older versions, you’ll need to specify a width according to a definite size (such as pixels). However, the most recent version of Opera (9.27) handles it fine, and I get the impression that Opera users tend to upgrade more frequently than, say, IE users.

Finally, we just float the elements left (to put them in a nice horizontal row), and give them some colors:

#navbar li:hover li, #navbar li.hover li {
	float: left; }
#navbar li:hover li a, #navbar li.hover li a {
	color: #000; }
#navbar li li a:hover {
	color: #357; }

And that’s it! You can see a working example here. This has been tested and works fine in IE 6+, Firefox 2, Safari (Mac and PC), and Opera 9.27.

The only portion of the mockup I wasn’t quite able to duplicate was a way to keep the primary menu item highlighted when the submenu was in use: because the nested list is absolutely positioned, the browser doesn’t seem to consider them a matched set any longer (except, it seems, in terms of default width). If anyone has a solution to this bit, I would love to hear about it!

Thanks to Andrea for inspiring a hopefully useful tutorial! And if you ever have a CSS-related question that you think might make a good article here, don’t hesitate to send me a message, either via my contact page or on Twitter. I can’t guarantee I’ll use every question posed, but I’ll do what I can as time and situation allow.

87 Responses

  1. aquafortis (reply)

    Thank you SO MUCH! The site is gorgeous and perfect now. :) I’ll definitely be reading your article on positioning. It’s one aspect of CSS that kind of hurts my brain, although what you said makes perfect sense.

  2. El Smurfa Diablo (reply)

    Great article! Have there been any responses on going another level or two on into the sub-menus?

    Thanks!

  3. Kristina (reply)

    Hi,

    Great tutorial, even after finding it a couple of years after it was originally written, I found it to be quite helpful.

    I have a question (for anybody reading this as well, not just for Rob). Is there a relatively simple way to add a sort of “saved state” function? To use Andrea’s mock-up as an example, if a user were to click on the “Current Exhibit” link in the navigation, is there a way to make it so that when the new page loads, the menu bar already has the “Exhibits and Events” submenu visible and the “Current Exhibit” link highlighted?

  4. Loc (reply)

    if I was on “Item One” page and would like to leave it highlighted, so people would know that I am on that particular page. How do I go about doing it?

    tks

  5. Simon T8W (reply)

    Hey is there a way to make the menu stay active while the dropdown is being hovered upon I mean like for instance the Exhibit and Events will stay black whiles the dropdown is active. I hope you understand what I mean. Please help me I’m sure this is a javascript thing but I can’t seem to do it :(

    P.S i love your dropdown menu and want to use it please reply soon

  6. welly (reply)

    This is great, but I need a little mod to make it fantastic!

    How would you go about making a “current” state, so that when a user clicks on a top level menu item, it’s sub navigation remains visible on that page? This would save users having to hover over the parent menu item to view it’s sub menu once again.

    This would have to be flexible enough that when a user hovers over a different pages parent menu item the corresponding sub navigation appears.

  7. mrsd (reply)

    Great tut! Was wondering if anyone could help me figure out how to center the sub menu instead of it being left aligned to the container. Any suggestions would be greatly appreciated!

  8. Pingback: xHTML/CSS Horizontale navigatiebalk mislukt - 9lives - Games Forum

  9. Pingback: Horizontal navigation bar fails :(

  10. sandi (reply)

    How does one keep the first tab open, so it features the sub menu.

    By open I mean, I would like to have the navigation’s first tab (only), appear hovered over, showing the sub menus…is there a script to feature this?

    Thanks,
    Sandi

  11. Sandra (reply)

    Anyone? please help!

    How does one keep the first tab open, so it features the sub menu.

    By open I mean, I would like to have the navigation’s first tab (only), appear hovered over, showing the sub menus…is there a script to feature this?

    Thanks,
    Sandi

  12. Pingback: Crooked Glasses ~ a slightly tilted view of the world

  13. Puky loopfiets (reply)

    Wow, This code is great.

    The dropdown is good. I am trying to add some round corners to it and that worked great to. Perhaps someone else would use that to?

    At this:

    -webkit-border-radius: 10px;
    -moz-border-radius: 10px;
    border-radius: 10px;

    to the “hover” sections. Then when you hover the menu item it will have rounded corners.
    good luck

  14. joy (reply)

    Thnks Rob!

    It was really help me. i have to hard implement with ur #navbar li code.. coz my design it’s very complicated for submenu

    AND finally i was fix my issue using ur code.

    Thnks you dude, once again, keep it UP!

  15. Pingback: A Useful Collection of CSS Dropdown Menus | Dzinepress

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>