CSS is wonderful stuff.  Really.  If you know what you’re doing, you can twist standard, boring HTML elements into almost any display state you like, all while maintaining the sort of semantic accessibility that’s so important for things like SEO and section 508 compliance.

One of my favorite examples is turning a standard HTML list into a menu.  As with all things, we begin by using HTML to define the kind of information being displayed.


<ul id="menu">
 <li><a href="/url1" id="menu_item_1">Menu Item #1</a></li>
 <li><a href="/url2" id="menu_item_2">Menu Item #2</a></li>
 <li><a href="/url3" id="menu_item_3">Menu Item #3</a></li>
 <li><a href="/url4" id="menu_item_4">Menu Item #4</a></li>
 <li><a href="/url5" id="menu_item_5">Menu Item #5</a></li>
</ul>

Simple, straightforward, boring, yet effective at communicating that this is an unordered list of links.  Take note of the IDs I’ve included; these will be important later.

Enter CSS to make this menu beautiful.  Let’s say we have an image prepared that is meant to serve as the menu.  And let’s say this image is using gradients or drop-shadows or a font that isn’t safe for use on the web, all good reasons to make it an image rather than text on a background.

We begin by defining the image, text and all, as the background of our menu item list and setting its width and height to exactly match the width and height of the image.  We’ll also need to break the list items out of their display state and redefine them as blocks.  While we’re at it, let’s hide the text; it’ll just be getting in the way of our beautiful menu image anyway.


ul#menu {
 background: url(/path-to-images/main-menu.png) top left no-repeat;
 height: 31px;
 width: 500px;
}

ul#menu {
 list-style-type: none;
}

ul#menu li {
 display: inline;
}

ul#menu li a {
 display: block;
 text-indent: -9999px;
}

Now, we’re going to need to move these anchors around in a minute, so we need to set their position to absolute.  To make it work correctly, we’ll also need to create a positioning context by setting the containing list’s position to relative.  Don’t worry; it’s not going anywhere.  This is just a trick that makes the list items move absolutely in reference to the menu rather than the whole page.


ul#menu {
 background: url(/path-to-image/main-menu.png) top left no-repeat;
 height: 31px;
 position: relative;
 width: 500px;
}

ul#menu {
 list-style-type: none;
}

ul#menu li {
 display: inline;
}

ul#menu li a {
 display: block;
 position: absolute;
}

Okay, we’ve got the image in place and the anchors redefined as blocks within it.  Now it’s time to position them.  Remember those IDs I defined earlier?  By referencing those, we can set each menu item’s unique width, height, and position.


ul#menu li a#menu_item_1 {
 height: 19px;
 left: 9px;
 right: 6px;
 width: 91px;
}

ul#menu li a#menu_item_2 {
 height: 19px;
 left: 107px;
 right: 6px;
 width: 91px;
}

ul#menu li a#menu_item_3 {
 height: 19px;
 left: 205px;
 right: 6px;
 width: 91px;
}

ul#menu li a#menu_item_4 {
 height: 19px;
 left: 304px;
 right: 6px;
 width: 91px;
}

ul#menu li a#menu_item_5 {
 height: 19px;
 left: 402px;
 right: 6px;
 width: 91px;
}

Finding these values can take a bit of work.  Often times, I’ll open the image up in PhotoShop and measure them out.  I may also use the Firefox Web Developer Toolbar’s Edit CSS function to place them incrementally.  Just apply a static background color to each menu item block, adjust the size and position until it’s where you want it, then remove the background color and save.

Voila!  Now you have a menu that is beautiful, fully accessible, and all-around… wait, what’s that?  You want rollover states, too?  No sweat.

You already have the entire menu defined.  Now all you need are images to swap in as backgrounds when the user mouses over the menu items.  Note that these should be the exact height and width of the menu items themselves, and should mesh seamlessly with the background image to keep your users none the wiser.  It helps to have a modified version of the original menu that you can cut into chunks for this purpose.

Once you have your rollover images ready, just define them as background images for the active and hover states of your existing menu items, like so.


ul#menu li a#menu_item_1:active, ul#menu li a#menu_item_1:hover {
 background: url(/path-to-images/menu-1-rollover.png) top left no-repeat;
}

ul#menu li a#menu_item_2:active, ul#menu li a#menu_item_2:hover {
 background: url(/path-to-images/menu-2-rollover.png) top left no-repeat;
}

ul#menu li a#menu_item_3:active, ul#menu li a#menu_item_3:hover {
 background: url(/path-to-images/menu-3-rollover.png) top left no-repeat;
}

ul#menu li a#menu_item_4:active, ul#menu li a#menu_item_4:hover {
 background: url(/path-to-images/menu-4-rollover.png) top left no-repeat;
}

ul#menu li a#menu_item_5:active, ul#menu li a#menu_item_5:hover {
 background: url(/path-to-images/menu-5-rollover.png) top left no-repeat;
}

Congratulations, your new menu is now beautiful, fully accessible, and dynamic, to boot.  Not to mention, your design is also fully removed from your HTML.  As a bonus for using a pure CSS solution, there’s absolutely no JavaScript to muddle up your cross-browser compatibility.

2 thoughts on “CSS Menus Made Simple

  1. The link to the example is apparently broken here. I’d love to see the example in action. Thanks!

  2. Hello John. Apologies for that. I lost those files awhile back when the site went belly up. If you want to see an example, take a look at the code on the utility menu (“About Us, History, etc.”) on AndysBurgers.net. This is one I built more recently and have been meaning to add to my portfolio. The menu items are inline blocks rather than absolutely positioned, so it’s not an exact match to the above code, but they do make proper use of sprites. Check the hover CSS for the menu items and you’ll see what I mean.

Leave a Reply

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