Creating an image-based menu with CSS

July 10, 2008 – 12:36 pm

The year is 2008 and I come upon a new site on a daily basis using the Javascript method of creating an image-based menu with rollover effects. I’m not talking flashy rollover effects like the types given to you by various JS libraries such as MooTools and script.aculo.us, I’m talking a simple image replacement effect. Invoking JS for this sort of thing is bulky and unnecessary when it can be done oh-so-easily with some straightforward CSS.

One highly used method, which I’ll cover another time, allows you to place browser-rendered text in that menu. That’s useful for many reasons, but sometimes you don’t WANT browser-rendered text because you’d like to use a non-web standard font but wish to maintain the text in place for screen readers and SEO. In this case we want to create a menu using the “Cooper Std” font. So let’s get started.

First, the markup:

<ul id="menu">
	<li class="home"><a href="#">Home</a></li>
	<li class="locations"><a href="#">Locations</a></li>
	<li class="about"><a href="#">About Us</a></li>
	<li class="contact"><a href="#">Contact Us</a></li>
</ul>

That’s all. It’s very short and semantically relevant.

For this sort of method it’s best to create your images as sprites (or one big sprite, up to you.) What I mean by that is to take the image for the normal state and the image for the hover state and place them on one image, like so:

Menu image sprite

Then rather than calling a different image for the hover action, you just call the same image with an altered background position. You’ll see what I mean in the code. This helps stop the flicker you would get when the browser tries loading the new image when hovering, and it also lowers the amount of requests made to the web server. It would be even better to place all of the images into one big sprite map, but for the sake of this tutorial we’ll just create one sprite per menu item.

This, without styling, will produce the following results, so anyone in a text browser will still be able to see your links. So let’s start adding some styles.

body {
	margin: 0;
	padding: 10px;
	font-family: Arial;
	background-color: #303136;
	font-size: .75em;
	color: #333;
}
ul#menu {
	list-style: none;
	margin: 0;
	padding: 0;
	height: 25px;
	width: 520px;
}
ul#menu li {
	float: left;
	display: inline;
	height: 25px;
	margin: 0 10px;
}

This will give your menu a bit more structure. What you’re doing here is creating the menu as an unordered list with static height/width dimensions. With text-based menus you generally want to stay away from hard-coded height properties to account for users who wish to increase or decrease their font sizes and not allow that to break your design, but in this case that’s not necessary since these are images.

We’re floating the list items to the left to create a “seemingly inline” list out of what are block elements. The “display: inline” property is there as a fix for IE6 adding a random margin.

Next thing we want to do is add the images to each class along with their corresponding width properties:

ul#menu li.home a {
	background: url(images/menu_home.png) 0 0 no-repeat;
	width: 67px;
}
ul#menu li.locations a {
	background: url(images/menu_locations.png) 0 0 no-repeat;
	width: 119px;
}
ul#menu li.about a {
	background: url(images/menu_about.png) 0 0 no-repeat;
	width: 115px;
}
ul#menu li.contact a {
	background: url(images/menu_contact.png) 0 0 no-repeat;
	width: 136px;
}

Note the background position, “0 0″, which is the same as “left top.” This will show the image in its normal state. However, we’ve got a little problem here. The browser-rendered text is showing up, and the menu image only spans as high and wide as the text itself. We can fix that pretty easily with the following snippet:

ul#menu li a {
	display: block;
	height: 25px;
	text-indent: -999em;
}

Setting the inline anchor element to a block element allows us to specify its height and width explicitly. The text-indent will move the text completely out of view. Google frowns upon using “black hat” tactics such as hidden text for SEO purposes, but when you’re doing this to a few words in a menu in which the images you’re replacing the words with say the same thing, I’ve never heard of them having a problem with that. It’s when you start hiding paragraphs of key words out of view that Google decides you’re no good.

The only thing left to do is the hover state. This will do the trick:

ul#menu li.home a:hover, ul#menu li.locations a:hover,
ul#menu li.about a:hover, ul#menu li.contact a:hover {
	background-position: bottom left;
}

See that? All you’re doing is moving the background’s position to the bottom left rather than the top left. It’s the same image, but it gives the illusion of loading a new one. No flicker.

View the menu

Final touches

If you’re using Firefox, when clicking a menu item you’ll see an ugly dotted line border around the element going all the way to the left edge of the screen. This is something the browser adds itself. There’s a way to get rid of it though, just add a hidden overflow property:

ul#menu li {
	float: left;
	display: inline;
	height: 25px;
	margin: 0 10px;
	overflow: hidden;
}

Also, if you want to specify a selected state for a menu item, just add an ID attribute called “selected” and add this CSS:

ul#menu li#selected a {background-position: bottom left;}

View the final menu with selected item

  1. 7 Responses to “Creating an image-based menu with CSS”

  2. Hey, just wanted to leave a note and say that this is a awesome article… Most of the tutorials for menus are for drop down javascript or drop down css menus and all I wanted was a menu where I can pick the font and it works good… This tutorial fits the bill.

    Thanks!

    By Anthony Parker on Sep 15, 2008

  3. Thanks for the nice tutorial.How about making a tutorial of creating a image hover menu,when the menu is hover,it will show an tab image with links inside it…Thanks…

    By handoyo on Dec 4, 2008

  4. Hey, thanks for the great tutorial. As you mentioned, how would you do it with it all being one sprite graphic?

    By Houston Graham on Oct 18, 2009

  5. This is great, just what I was looking for. But I also need to do the same thing for a right aligned horizontal menu. How would you change the css?

    By Chris Cooke on Dec 22, 2009

  6. Chris,

    Simply change the “float: left” to “float: right” in the “ul#menu li” element. This will reverse the order of appearance for your HTML list elements so just reverse their order in the HTML.

    Alternatively, you can set a width to the “ul#menu” element and float that to the right and avoid the previous method.

    By Orlando on Dec 23, 2009

  1. 2 Trackback(s)

  2. Dec 8, 2008: Adding a drop down to our image-based CSS menu | Word up, son!
  3. Dec 10, 2008: Image-based menu with CSS color property | Word up, son!

Post a Comment