Pure CSS menus

pure CSS menusSee it in action

This is not an original trick, of course. But it’s a clean and simple one, which I want to blog about mainly so that I always have it handy when I need it again.

Using OS-style dropdown menus across the top of your website is as common as anything, and they’ve been in use ever since developers first figured out how to implement them with JavaScript and absolute positioning. Then CSS advanced a little more, and with the advent of :hover pseudo-styles we could finally build the whole thing without any JavaScript at all.

The trick, such as it is, is to build your menus out of nested ordered lists. jsFiddle automatically applies a reset.css to the page, and so do I whenever I’m building a site. If you choose not to, though, just know that you need to add a style of list-style: none; to your menu’s ul elements before you do anything else.

Let’s start with the top-level menu:

<ul id="topmenu">
    <li><a href="#">Menu 1</a></li>
    <li><a href="#">Menu 2</a></li>
    <li><a href="#">Menu 3</a></li>
    <li><a href="#">Menu 4</a></li>
    <li><a href="#">Menu 5</a></li>
</ul>

Obviously we don’t want to stack them vertically, but horizontally. There are two ways to achieve this: give the list elements a CSS style of display: inline-block or display: float. inline-block is better for a few subtle reasons, but older versions of IE won’t recognize this style, while they will recognize float. When possible, try to preserve backwards compatibility.

Unfortunately, this squishes all the elements up against each other. For now, we’ll solve that problem by giving each list item a fixed width as well:

#topmenu > li {
    float: left;
    width: 80px;
}

Notice the uses of > to indicate we’re only styling direct children of #topmenu. That’s because we’re going to add more list items as submenus:

<ul id="topmenu">
    <li><a href="#">Menu 1</a>
        <ul>
            <li><a href="#">Submenu 1-1</a></li>
            <li><a href="#">Submenu 1-2</a></li>
            <li><a href="#">Submenu 1-3</a></li>
            <li><a href="#">Submenu 1-4</a></li>
        </ul>
    </li>
    <li><a href="#">Menu 2</a>
        <ul>
            <li><a href="#">Submenu 2-1</a></li>
            <li><a href="#">Submenu 2-2</a></li>
            <li><a href="#">Submenu 2-3</a></li>
        </ul>
    </li>
<!-- etc. -->
</ul>

Notice that the nested <ul>s are inside the <li> tags, not outside of them. It’s illegal HTML to make an unordered list a direct child of another unordered list.

We want these submenu items to stack vertically, so we don’t need to float anything. We do, however, need to hide them from view:

#topmenu > li > ul {
    display: none;
}

To display them as the user mouses over the main list items, we apply a :hover pseudo-style:

#topmenu > li:hover > ul {
    display: block;
}

The magic comes from the fact that our submenus are nested inside the list elements of our main menu — so as long as our mouse is positioned over a submenu, it’s also hovering over the main list item, and the submenu continues to be displayed.

If we want the submenus to stick out a little like they do in (insert your favorite OS here), we can adjust the width of the submenu list items:

#topmenu > li > ul > li {
    width: 100px;
}

Now for a minor but useful trick: add display: block to the hyperlinks inside the menu. This forces the hyperlinks to expand invisibly to fill the entire width of the list item. The result is that the user doesn’t have to mouse directly over the word in the menu or submenu in order to trigger a hover; they can mouse anywhere over the list item, just like an OS menu:

#topmenu a {
    display: block;
}

Now just a few finishing touches. Let’s add a background color to the hyperlinks and a :hover style to change it as the user mouses over a submenu:

#topmenu a {
    display: block;
    background: #ddd;
}
#topmenu a:hover {
    background: #eee;
}

And last but not least, we’ll absolutely position our menu at the top of the page and push whatever element comes after it down a few pixels out of the way:

#topmenu {
    position: absolute;
    top: 0;
}
#topmenu + * {
    margin-top: 24px;
}

And there we have all the basics in place. There’s a lot of styling to go yet, but there always is. Customize according to whatever your site design requires.

http://jsfiddle.net/mblase75/7bVbC/

Leave a Reply

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

*