Graphic Nav Bars Using One Image Only
Remember when, for a five-section graphic nav bar we would have 15 images … five images for the Off state, five images for the Hover state, and five images for the On state? And then we cleverly had just five images - one for each section - in which we combined the Off, Hover, and On states? Well lately I’ve been taking this trend to it’s logical conclusion … one image for everything. That’s right, for everything.
Using background positioning in CSS, this is actually pretty easy to accomplish. Now you only have one image to create, and it has the extra bonus of taking care of any pesky little IE flicker problems. Here’s how to do it …
First Create Your One Image
Click on the image above to open the full-sized version so you can get a closer look at it. This was pretty easy to create in Photoshop. The first set of buttons on the left, the Off state, I created using one large rounded corner shape, added a gradient, and inner shadow, and a stroke to it in the Layer Style pallet. Then I threw some dark and light lines for dividers on top of that. Lastly came my text, to which I added subtle drop shadow and a stroke. After that I just tripled my canvas width, duplicated all my layers twice, and changed some of the color and gradient values for the various effects to get my Hover and On states.
The XHTML
I’m going to give an ID to my unordered list and a class to each LI. Additionally I’m going to give a unique ID to the BODY tag. I’m going to do this for each page, and then use the cascade to determine which button uses the On state.
<body id="bd-home">
<ul id="nav">
<li class="nav_home"><a href="index.htm">Home</a></li>
<li class="nav_about"><a href="about.htm">About</a></li>
<li class="nav_products"><a href="products.htm">Products</a></li>
<li class="nav_support"><a href="support.htm">Support</a></li>
<li class="nav_contact"><a href="contact.htm">Contact</a></li>
</ul>
</body>
The CSS
First I’m going to define the UL - give it a width, a height, center it, and give it a background of the nav image itself. This is what will eliminate any flicker in IE … if it takes a moment of the background we put on the anchor tag to show up on hover, no worries! Next I get rid of bullets and give the LIs a fixed width and height, and float them left. Now the anchor tags get a display of block so that I can also give them a fixed width and height. I also indent the browser text in order to hide it.
#nav {
width:460px;
height:31px;
margin:0 auto;
background:url(nav.gif) no-repeat;
}
#nav li {
list-style-type:none;
float:left;
width:92px;
height:31px;
}
#nav a {
display:block;
width:92px;
height:31px;
text-indent:-9999em;
}
Next the individual nav items get their hover states defined. All I’m doing here is changing the horizontal position of the background image that is placed on the anchor tags when hovered. It’s that simple. I’m sure more mathematically inclined folks could figure out a real easy way to determine the negative horizontal pixel position, but I pretty much used good old fashioned guessing and adjusting. ;~)
.nav_home a:hover {
background:url(nav.gif) no-repeat -460px 0;
}
.nav_about a:hover {
background:url(nav.gif) no-repeat -552px 0;
}
.nav_products a:hover {
background:url(nav.gif) no-repeat -644px 0;
}
.nav_support a:hover {
background:url(nav.gif) no-repeat -736px 0;
}
.nav_contact a:hover {
background:url(nav.gif) no-repeat -828px 0;
}
Next I’m going to define what happens on each page, so that the appropriate nav items are On. Once again, I’m just changing the horizontal position of the background image of the anchor tag that corresponds with the page that we are currently on.
#bd-home .nav_home {
background:url(nav.gif) no-repeat -920px 0;
}
#bd-about .nav_about {
background:url(nav.gif) no-repeat -1012px 0;
}
#bd-products .nav_products {
background:url(nav.gif) no-repeat -1104px 0;
}
#bd-support .nav_support {
background:url(nav.gif) no-repeat -1196px 0;
}
#bd-contact .nav_contact {
background:url(nav.gif) no-repeat -1288px 0;
}
Finally, in order to make sure that the Hover state doesn’t show up when hovering over On items we have this bit of CSS:
#bd-home .nav_home a,
#bd-about .nav_about a,
#bd-products .nav_products a,
#bd-support .nav_support a,
#bd-contact .nav_contact a {
background:none;
}
That’s a Wrap!
And that’s it! Pretty simple, actually. One image, one list, one small css file, and voila!
Note that this example show a nav bar where all items are the same width. I did this for simplicity, but varying widths could easily be accommodated … just define them for each nav item in the CSS.


October 9th, 2007 at 6:11 pm
Ah, I’ve missed your tutorial posts; they’re always so well-written and easy to follow.
This is definitely one of my favorite CSS techniques. I remember the days of preloading images into a JavaScript array in order avoid that annoying first hover. Having to wait 5 seconds for your “hover” state to show up kind of killed the cool factor.
Hope you continue gracing us with your posts! :-)
October 9th, 2007 at 7:05 pm
Have you seen this CSS sprites generator? For those wishing to use your idea, it will combine existing images into one. It also calculates the offsets for the hover states for you.
October 10th, 2007 at 8:52 am
Hi David:
No I hadn’t seen that. I don’t really understand what it does since I have no such images easily on hand to give it a whirl myself. I wish they had an example of what it would do!
October 12th, 2007 at 2:21 am
Great work Mani, I’ve been reading your tips for a long time and I can say that they’re quite an inspiration source. Thanks for sharing your knowledge.
October 12th, 2007 at 5:43 pm
Yes. That page is a bit sparse.
Sprite generators seem to be all the rage. There is one at website- performance.org as well. This at least explains what a sprite is. The comments on Stuart Collville’s site (link at bottom of page) complains about the same thing as you do - no examples. Hmm.
October 16th, 2007 at 10:22 pm
It seems that you can use this technique of combining images for more than multi-state menu backgrounds. I just came across an article about positioning IMG elements made from a combined graphic.
This article has examples. :)
November 21st, 2007 at 10:28 pm
Wicked awesome! Thanks for this whirl through some brave new CSS wizardry!
December 21st, 2007 at 8:12 pm
Hi,
I was testing your method and I worked fine in IE7 but not in firefox or safari. I both of these it displayed a chunk of the On segment of buttons. As if it were like 30px too wide.
What up wit dat?
Let me know,
Fernando
January 6th, 2008 at 3:46 pm
Your example is clear and well written and does a good job explaining CSS I’ve seen on other pages.
While it works well in IE, FireFox has a problem. It appears to offset the position that is supplied for the hover and ON postions by about 40px to the right.
Do you have any idea about why this might happen?
Thanks,
Rich
January 13th, 2008 at 9:59 pm
Mani! Come back! We miss you!
January 24th, 2008 at 11:19 am
Rachel,
This is so kind of you!! I do want to come back to blogging, but my life has become so full and my focus has shifted and so it’s hard for me to figure how it will all work. I’m hoping that in the future a way will reveal itself naturally and I’ll start a new era on the Sheriar Designs blog. For now, I’m trying to simply not feel too guilty about abandoning it all.
Thanks so much for your support,
Mani
March 15th, 2008 at 1:39 pm
I just found this page while browsing through CSS Zen Garden. This was a very helpful post!
June 23rd, 2008 at 11:30 am
Nice tutorial!