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:

    <li><a href="#">Navigation item 1</a></li>
    <li><a href="#">Navigation item 2</a></li>

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:

navigation example

“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:

navigation PSD

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: url("navigation.png") no-repeat 0 0 transparent;
    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.

    <li><a href="#">Navigation item 1</a></li>
    <li id="navigation2"><a href="#">Navigation item 2</a></li>

ul li#navigation2 a {
    width: 60px;
    background-position: -34px 0;

Now our regular old HTML unordered list is starting to get somewhere:

navigation raw

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.

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;