Easy, Breasy, Beautiful CSS Menus
I was working on a project for work these last couple of days, and I was getting serious bruises on my forehead as well as making a considerable dent on my desk. My plan was to bring our navigation menus to at least the 21st century (my personal target was 2007, but what can you do?). My initial objective was to use pure CSS drop-down menus, but that quickly turned out to be a real adventure.
For some strange and unexplainable reason, IE doesn't recognize the hover selector on anything else than links (<a>). So I ventured in the realm of .htc files, and got the hover selector to work. Then, I realized these floating and absolutely positionned divs were no different than any other floating and absolutely positionned divs: they appear below <select> drop-downs. Rewrite CSS, remove hover behaviors and switch to Javascript (actually quite easy thanks to jQuery) so I could use an <iframe> hack. Now everything was going fine, except for one thing: the design would simply break under IE! For some reason, the problem was never quite the same. Sometimes, hovering over the menu item would cause an extra line to be appended before the following content. Other times, it would append an empty line between the list items (adding a border to the list would correct this (??), but hovering over the borders would hide the menu (??)). Once I felt I was truely defeated, I started googling for CSS menus. I found several working and cross-browser examples. However, they all seemed to resort to complicated solutions: transparent gifs (yup, they still exist), tables (for a menu??), IE conditional statements (don't get me started...). Why all this trouble when a simple unordered list and maybe a dozen CSS lines make it all work in any standards-compliant browser? So I gave up. I'm not afraid to say it. Actually, I'm quite happy with the alternative. It's markup is simple, and all it needs to work is a little CSS. But the main selling point is that it's cross-browser, and even degrades gracefully. I give you Easy, Breasy, Beautiful CSS Menus. The first thing we want to do is setup a markup for our menu. If you take the time to think about what a menu is, you'll find out that it's basically just a list of links. With that in mind, let's build:
<ul class="menu">
<li><a href="#">Page 1</a></li>
<li><a href="#">Page 2</a></li>
<li><a href="#">Page 3</a></li>
<li><a href="#">Administration</a></li>
</ul>
Viewing this any browser, this is what we would get:

This is the basic structure of our menu, and what someone with a text browser or with CSS disabled will see. We obviously don't want to keep this look and feel, so we'll start by removing the <ul>'s default styles.
list-style-type: none;
margin: 0;
padding: 0;
}

We'll then line up our list-items one beside the other. To do so, we apply the float: left property to them. We'll also add a border to the top of our unordered list.
list-style-type: none;
margin: 0;
padding: 0;
border-top: 1px solid #1A3F95;
}
ul.menu li {
float: left;
}

Notice that our links are now horizontal. However, they're a little bit close. To take care of that little problem, all we have to do is add some margin to our list-items.
float: left;
margin-left: 5px;
}

Our little project is starting to look like something. Now let's start applying some style to our little menu. We'll add a border and some padding to our list-items. Notice that we override the top border with none. That's because there already is a top border (from the <ul> element). While we're at it, let's make the links prettier. For now, we'll settle with removing the underline with text-decoration set to none and changing the default color.
float: left;
margin-left: 5px;
border: 1px solid #FC0;
border-top: none;
padding: 2px 10px;
}
ul.menu a {
text-decoration: none;
color: #1A3F95;
font-weight: bold;
font-size: 80%;
}

All good menus have a little mouseover effect when the user's cursor is over a link. Let's not be different! We'll stay simple for this example, and change the background color of the links.
background-color: #EEE;
}

Notice anything wrong? That's right, the background color doesn't apply to the entire menu item. To get this fixed, all we need to do is move the padding from the list-items to the links.
float: left;
margin-left: 5px;
border: 1px solid #FC0;
border-top: none;
}
ul.menu a {
text-decoration: none;
color: #1A3F95;
font-weight: bold;
font-size: 80%;
padding: 2px 10px;
}

And there you have it. Adding a link is as simple as adding a new list-item. And if you want to change the styles, they're all in one place and easy to update. Here is the entire source for this example:
ul.menu {
list-style-type: none;
margin: 0;
padding: 0;
border-top: 1px solid #1A3F95;
}
ul.menu li {
float: left;
margin-left: 5px;
border: 1px solid #FC0;
border-top: none;
}
ul.menu a {
text-decoration: none;
color: #1A3F95;
font-weight: bold;
font-size: 80%;
padding: 2px 10px;
}
ul.menu a:hover {
background-color: #EEE;
}
</style>
<h2>Easy, Breasy, Beautiful CSS Menus</h2>
<ul class="menu">
<li><a href="#">Page 1</a></li>
<li><a href="#">Page 2</a></li>
<li><a href="#">Page 3</a></li>
<li><a href="#">Administration</a></li>
</ul>
Easy, Breasy, Beautiful CSS Menus - With Images!
For those of you that like their menus to be a little, well, prettier, there is a solution: Easy, Breasy, Beautiful CSS Menus - With Images! The idea behind this alternate version is that we don't want borders to be the only thing to enhance the visual aspect of our menus: we want images. And, since we want our menu to remain as flexible as possible, we'll have to add a couple of layers to our example. The first step is preparing for varying content. Our design will have to be able to accommodate varying quantities of text, and the best way to do so is with the Sliding Doors of CSS technique. Hence, we prepare four images (two for idle, two for mouseover).

The Sliding Doors technique requires two different elements (one for each background image). Since we'll want these background images to react to our links' hover selector, we'll need to apply one background image to the link itself, and another to a child element. In our previous example, our links didn't have any child elements, so we will have to add one.
<li><a href="#"><div class="menu_wrapper">Page 1</div></a></li>
<li><a href="#"><div class="menu_wrapper">Page 2</div></a></li>
<li><a href="#"><div class="menu_wrapper">Page 3</div></a></li>
<li><a href="#"><div class="menu_wrapper">Administration</div></a></li>
</ul>

Notice that our link is being pushed down. This is because you can't have a block-level element within a inline one. Luckily for us, this is easy to fix. All we need to do is a display: block property to our links:
text-decoration: none;
color: #1A3F95;
font-weight: bold;
font-size: 80%;
padding: 2px 10px;
display: block;
}

Next, we'll want to apply our background images. Removing our original list-item borders is probably a good idea as well at this point.
float: left;
margin-left: 5px;
}
ul.menu a {
text-decoration: none;
color: #1A3F95;
font-weight: bold;
font-size: 80%;
padding: 2px 10px;
display: block;
background: url('/blog/images/Easy-Breasy-Beautiful-CSS-Menus_right.gif') bottom right no-repeat;
}
ul.menu a div.menu_wrapper {
background: url('/blog/images/Easy-Breasy-Beautiful-CSS-Menus_left.gif') bottom left no-repeat;
}

Now this problem looks familiar. Our left background image isn't lining up with our right one. Remember we had applied some padding to our links earlier, because the background color wasn't applying to the entire list-item? We just need to do the same thing here, except one level further: move the padding from the link to the menu_wrapper div.
text-decoration: none;
color: #1A3F95;
font-weight: bold;
font-size: 80%;
display: block;
background: url('/blog/images/Easy-Breasy-Beautiful-CSS-Menus_right.gif') bottom right no-repeat;
}
ul.menu a div.menu_wrapper {
padding: 2px 10px;
background: url('/blog/images/Easy-Breasy-Beautiful-CSS-Menus_left.gif') bottom left no-repeat;
}

All that's left to do now is apply our hover selectors.
background: url('/blog/images/Easy-Breasy-Beautiful-CSS-Menus_right_over.gif') bottom right no-repeat;
}
ul.menu a:hover div.menu_wrapper {
background: url('/blog/images/Easy-Breasy-Beautiful-CSS-Menus_left_over.gif') bottom left no-repeat;
}

And there you have it! A fully functional, flexible, easy to update/manage CSS menu. I hope you enjoyed this episode as much as I did posting it. Let me take advantage of the fact that I've got everyone's attention to wish you all a happy new 2008 year! I'll see you all on Friday!
Is this dream really a dream? Is this reality really reality?


There are no comments for this entry.
[Add Comment]