Thursday, May 31, 2007 (Simple Navigation with CSS Image Replacement)
No silly javascript, no crazy coding, no waiting for images to load. Just some good old fashioned HTML, CSS, and a single image. Once you do this, you’ll never make your web site navigation any other way.
Warning: If you aren’t into front-end coding for the web, you may want to skip this. For those that are (I love you that much more), this has been covered elsewhere and covered much better, so hopefully this is old hat.
Can’t wait for the results? Here’s the final navigation with CSS image replacement, used in the real world. Please, disable styles, view source.
How to do it
First, you should already be in the habit of making your navigation using unordered lists. If you’re not doing it, now is the time to start.
Setup your navigation in an unordered list:
<ul> <li><a href="#">Navigation item 1</a></li> <li><a href="#">Navigation item 2</a></li></ul>Next, add a little CSS to control your list items:
ul li { display:block; float:left; list-style-type:none; }Such basic CSS results in:
With a few margin and font adjustments, you could stop here and you’d have yourself a nice clean navigation. But only nerds (and search engines) like such bland, basic, HTML navigation. We’re here to please our designers and clients. So Let’s turn this basic HTML into something so sexy they’ll shed tears at first sight. Something that will look like this:

“Oh, and can you make it kind of POP on hover?”
Why yes; in fact we can.
Make yourself an image that contains both the :link state and :hover state of all your navigation items. Better yet, teach your graphic designer to do this for you. In the example below, the :link states are on top and their :hover directly below. Both areas must be the same height.
Here’s what the image looks like, with some guides:

You’ll then export that file and use it as a background-image for all of the links in your unordered list. Here’s where your guides come in handy. In this example, the link area has a height of 66 pixels and the first list item is 34 pixels wide, so we’ll setup some CSS defaults for the anchor tags:
ul li a { display:block; height:66px; width:34px; background:transparent url(navigation.png) no-repeat 0 0; text-indent:-9009px; }You’ll notice the text-indent:-9009px; That pulls the text out of sight and out of mind. Make way for your pixel princess!
My second list item is wider, so let’s give that one an ID and change the width. We also need to pull the background-image 34 pixels to the left, since that’s where the image for the second list item starts.
<ul> <li><a href="#">Navigation item 1</a></li> <li id="navigation2"><a href="#">Navigation item 2</a></li></ul>ul li#navigation2 a { width:60px; background-position: -34px 0; }Now our regular old HTML unordered list is starting to get somewhere:

Now for the :hover & the real magic
All that’s left is to move the background-image on :hover. Since our list items have a height of 66 pixels, we’ll move the background-image up 66 pixels for each list item:
ul li a:hover { background-position: 0 -66px; }ul li#navigation2 a:hover { background-position: -34px -66px; }You’ll now have the start of one sweet ass, simple navigation:
Throw some margins in there for spacing, or add whatever tweaks you deem necessary. The final step is to laugh at how simple this is, and never look back.
Carry this same process throughout the rest of your navigation and you’ll end up with something so search engine friendly you’ll now be crying along with your client.
Here’s the final navigation with CSS image replacement, used in the real world. Please, disable styles, view source. If you look carefully, you’ll notice that once the client asks you to change the order of the list items, it’s as simple as moving around your LI’s in the HTML into any order you’d like.
If you have improvements, ways to make this more efficient, or you’d just like to share how long you’ve been using this technique for and how old it is; Please, leave your comments.
Update:
To get your hover state to stay in the hover state, to mark the users currently selected page for example, I like to use an ID or class on the body tag.
For example, on a contact page add:
<body id="contact">And in your CSS hover state, add a line for body#contact:
ul li a:hover,body#contact ul li a { background-position: 0 -66px; }Did you find this post helpful?
Posted in code, interweb / 54 Comments
Powered by WordPress using the Minimal, Really Theme. Inspired by Things
Mike Tomasello #
Used the same technique on a site I made a few weeks ago.
I did though, begin to wonder if it would be easier to have each as an individual image rather than one big image for all (so that background-position changes could be applied to all [the x values would be 0 for all]).
When you have say 7 menu items each with a different standard image, a :hover, :active, :focus, and selected page, the CSS background-positions start to add up.
j bird #
hahahah!
that was easy. heard about it about a month ago. But this is the first place that described how it works. Thanks.
Bill #
Even apple uses a single image!
Kristof Gheyssens #
I’ve followed your instructions, image replacements works nicely, BUT my navigation div is 800px as is the sum of all my ‘s, as you can see (http://www.puregraphx.be/security) the last list item is thrown to the next line. Please help!
Thanx
Mark #
WOW! Works like a dream as you said!
BUT… How do you get a image to stay like the hover state. If your in that section. so the navigation button stays highlighted. like apple :o)
I have tried creating a style with a different name which I change on each section to say highlight this.. with the same styles as the a:hover but it doesn’t work??
might be a good addition to this code?
Thanks
Bill #
Mark – What you need to do, is on each of your sites pages, add an ID or class to your body tag.
For example, on a contact page add:
body id=”contact”
& in your CSS hover state, add a line for body#contact:
ul li a:hover,
body#contact ul li a {background-position:xxx;}
Make sense?
PhiL #
Thanks for this tutorial! Works perfectly!!
PhiL
PhreQuencYViii #
This is awesome! But my question is, how would you center a navbar like this? I have one made, but I can’t get it to center on the top.
Bill #
Phrequencyviii – you would center the entire UL, perhaps by putting it in a block that has text-align: center in it, and setting your UL to have right & left auto margins with a set width.
Something along these lines:
div.nav-wrapper {text align: center;}
ul {width: 200px; margin: 0 auto;}
KBG #
Thank you so much. Camxso
Deena #
I knew this was possible but I could never get it right. Thanks for explaining it so well!
Chet #
Thanks dude, exactly what I was looking for!
Jeff #
I’m trying to convince my designer that this technique is better than having a bunch of smaller images. Thanks for the explanation.
craig #
hello, im building my first web site and was trying to accomplish this example on image roll overs but it didn’t seem to work and i’ve been trying everything, is there some catch im missing#???
craig #
scrap that, works now but every time i try to center it on my page it doesn’t center it properly beacuse of the li dot to the left of the image, any ideas???
JP #
Brilliant and simple–thanks so much!
Josh #
I had a problem when i wrapped my UL list inside a div tag it was being pushed away from the edges. I added little css to the UL and it solved my problem.
-----------------------
#nav{
margin:0px;
padding:0px;
}
ingilizce #
CSS “Cascading Style Sheets” Lessons
css list style Properties and examples — http://css-lessons.ucoz.com/list-css-examples.htm
Bradfields #
Awesome post, been pissing about trying to get a nav to work all morning, read this and it was sorted within 5 minutes.
Much respect to you sir!!
Richard J Keys #
This was one of the first things I learned in “the real world” after leaving university.
Excellent tutorial, especially the comment about adding an id to the body of each page – sure beats my messy solution! :p
ian #
Hi, Thanks for the walk through. I have managed to surprise myself! I have one problem though. On your update you show how to set a image to be “on” when the user is on the page. I cannot get this to work. When I apply this to the hover code and set the body id, all, the images get set to the same thing! I hope this makes sense and you could point me in the right direction. Thanks very much. Ian
joe #
dude, brilliant.
Tison #
Thanks for the article – not only is this a great CSS Image Replacement tut, but a solid CSS mouseover tutorial as well. Gave you some trackback love, Thanks again
Nippy #
Many thanks for sharing this, just what I was looking for.
Jenn #
This is an awesome tutorial…it was the best one I found online so far. Any ideas on how to implement this technique in dreamweaver using PHP? All the sites I’ve found have not been helpful as they all use multiple images and spans to achieve this affect and the php code is written for that instance.
Bill Keller #
Jenn While I don’t use Dreamweaver, its certainly possible to use any tool to output what you’ve done here. Good luck & thanks!
Luiz Lopes #
This is great, just what I was looking for.
Thank you for sharing it.
Marty #
Hi Bill.
I’m having two css image navigations on one page. Is there any tips you can give for having two buttons in two different navigations stay in rollover state?
Bill Keller #
Marty – Use a unique class on your second navigation and select with CSS accordingly.
John #
I’m having the same problem a Kristov – and the question was never answered.. please help…I’ve followed your instructions, image replacements works nicely, BUT when i put the ul’s in a containing div that is the same width as the nav bar itself it forces the last image down below the rest. Any ideas?
Thanx
Bill Keller #
John – It sounds like your UL is actually wider than the containing div. I recommend resetting margins and paddings to zero on all items first, using * {margin: 0; padding: 0;}
anna #
Hey there.
I’m having the same issue as ian. I have 4 pages and when I use the IDs for each to keep the hover state on the current page it places it at the first link. Another words, I can’t seem to change the location of the second, third, or fourth link. They all show up on top of the first link. Solution?
Thanks!
Bill Keller #
Anna – Are you adding a unique ID to the body of each HTML page? And then adding the appropriate CSS?
For example:
body#navigation2 ul li#navigation2 a {}
kaye #
do you know that i love you? i am much closer to opening our e-store because of this.
Zaxx #
[quote]Update:
To get your hover state to stay in the hover state, to mark the users currently selected page for example, I like to use an ID or class on the body tag.
For example, on a contact page add:
And in your CSS hover state, add a line for body#contact:
ul li a:hover,
body#contact ul li a {
background-position: 0 -66px;
}
[/quote]
This way, if there’s any other unordered list in the page, it will be affected too. Any other solution?
Bill Keller #
Zaxx – Use a class name on your UL so that other UL’s aren’t affected. Fore example: ul.navigation {}
Daniel #
This tutorial is pure gold. Keep up the good work!
Mark D. MacLachlan #
Awesome tutorial, thank you. Hoping you can help me figure out something with this. What I want to do is add a left and right image where my text will go in the middle. This will let me have my image dynamically appear to stretch.
My WordPress blog does this somehow and I’ve been trying to duplicate the affect with HTML and CSS. Any help would be greatly appreciated.
Bill Keller #
Mark – That sure is possible, but it’s an entirely different tutorial altogether. Have a look at A list Apart’s sliding doors of CSS.
Huw Rowlands #
This tut has helped over and over again. No other one helps just as much as this one does. Thanks!
Julie #
“Update:
To get your hover state to stay in the hover state, to mark the users currently selected page for example, I like to use an ID or class on the body tag.
For example, on a contact page add:
1.
And in your CSS hover state, add a line for body#contact:
1. ul li a:hover,
2. body#contact ul li a {
3. background-position: 0 -66px;
4. }
”
Thanks for the tutorial. It’s very helpful. I’m relatively new to a lot of this. Regarding the update quoted here, what is recommended when the image replacement navigation is in a div tag, versus floating loose in the body? The body ID isn’t working for me. Would a class solve this? Thanks much!
Bill Keller #
@Julie, glad you’re finding it helpful. This is just a normal CSS selector. You can use any selectors necessary to “get your navigation to stay in the :hover state”. Follow the normal CSS selector rules & you’ll be in good shape.
In the example I provide, any Anchor tag inside of an LI, inside of an UL, on a page with a Body id=”about” will be selected.
@gillian #
Thanks so much! This was really helpful!
gogetsome #
Great solution! I have the main navigation working perfectly. I want to also have a sub nav using the same technique. I have that set up and have everything working as expected except the hover state for the sub nav. How can I make the main nav section active and also have the sub nav page also active???
Richard #
Thanks for a great tute – have got it working fine but was wondering how to incorporate a drop down menu in the navigation. I’ve tried using a path of id’s to target the hover state and then trigger a drop down. I can sort of get it working at the link here but the hover state is the whole nav menu area but for this exercise I only want the drop down to appear when the discography link is hovered over. Also it’s repeating the image of the main nav as a background in the submenu.
Bill Keller #
Richard, you’ll need to fix the HTML for your sub-navigation, it should be:
<li id="discography-nav"><a href="#">Discography</a><ul class="dropdown"><li><a href="#">Topic 1</a></li></ul></li>And to set a new background image on .dropdown, you’ll just need to get more specific with your CSS selectors, for example:
#blackEyedSusanNav li ul.dropdown a {background-image: foo;}Then you can move on to tackling the next steps.