Elliot Jay Stocks is a designer, speaker, and author. He is the Creative Director of Adobe Typekit, the founder of typography magazine 8 Faces, one half of Viewport Industries, and an electronic musician.

CSS Tutorial: better nav image replacement

Posted on 27 October 2007 54 comments

Article illustration for Tutorial: better nav image replacement

A few months ago, I wrote a tutorial for Computer Arts magazine (in issue #137) that showed readers how to create a good-looking navigation system with some clean, simple, and semantic code. Most importantly, it only used one image file; a technique often referred to as ‘CSS sprites’. There are many great tutorials out there that deal with this subject, but as several people have asked me to reproduce this article, here’s my take on it (the example site being loosely based around the design I did for trojanrecords.com). By the way, this version also has some extra information there wasn’t room to print into the original article.

Before beginning the tutorial, you’ll need to download this ZIP file, as it contains all of the supporting material. Enjoy!


If you’ve laid out a site using CSS, chances are you’ve probably encountered the world of image replacement, using properties such as background-image and text-indent to replace standard text-based elements with interesting images. The aesthetic advantages are obvious, but image replacement is also a great technique because it means you can keep your clean markup intact. It works extremely well with navigation systems, particularly because – when using CSS’ :hover pseudo-class – it completely eliminates the need for those messy Javascript rollovers that litter the web of the past.

However, using a number of images for your navigation items (e.g: buttonOne-up, buttonOne-over, buttonTwo-up, buttonTwo-over, etc.) is still relatively messy and results in a delay (a blank space) in the ‘over’ image being displayed. Another drawback is the amount of work required to slice up each button image: usually the nav would have three different images for each button state, and that’s a lot of images and a lot of hassle.

So in this tutorial, we’ll do things differently: one single image will be used for all of our buttons and all three of their states (up, over, selected), our CSS will handle all the important stuff, and you’ll end up with some very neat, accessible, standards-compliant markup… as well as a great looking nav.

Please click on the thumbnails below to view the full-size screenshots.

Step 1

Thumbnail of screenshot for step 1

Open step1.psd. To focus on the navigation only, the document has already been cropped to the correct area. In order to distinguish the ‘hit’ area of each nav item, and to get accurate measurements, we’ll add vertical guides between each button (View > New Guide).

The look and feel of our example site is built around a rough / torn paper kind of look, and with a design a like this, you can see why we might want to use graphics for navigation rather than plain text. Ignore the logo and other elements in the header as we won’t be covering them in this tutorial – this is just to give context to the nav we’re going to build, and to demonstrate why we’re using graphics rather than plain text.

Step 2

Thumbnail of screenshot for step 2

Using the ‘Canvas Size’ utility (Image → Canvas Size), expand the height to 3 times the amount of the document. In this case, that’s 123px (or 41px + 41px + 41px). Use the arrows to make sure your canvas extends from the top.

Step 3

Thumbnail of screenshot for step 3

Duplicate every layer twice and move the copies down 41px from the top. Repeat, and move the second copies down 82px. Now edit the states: white text for ‘up’, a red background for ‘over’, and orange with a red background for ‘selected’. Add 2 horizontal guides: one at 41px and one at 82px, and your file should resemble step3.psd.

Step 4

Thumbnail of screenshot for step 4

Turn on snapping for guides (View → Snap and View → Snap To → Guides), create a selection around the NEWS button, hit command-c (Mac) or ctrl-c (PC) to copy, and then command-n (Mac) or ctrl-n (PC) to create a new document. This process allows you to quickly take measurements, and in the image above, you’ll see that the width and height of the NEWS button is 70px and 41px respectively. Therefore the distance from the left hand side to the SHOP button is 70px.

Note: in order to take measurements, you must have a layer selected that covers the whole area you want to measure. For this purpose, it’s usually best to create a flat colour layer underneath everything else and always select that before you copy.

Step 5

Thumbnail of screenshot for step 5

Create a new CSS file and make a note of each Photoshop measurement as a CSS comment. Keep repeating the process until you have a full set, and after you’re done, use the same process to measure the width of each button. Your handy notes should resemble the text in step5.txt.

Step 6

Thumbnail of screenshot for step 6

Create a new HTML file and paste in the markup from step6.txt. Each li has its own unique and meaningful ID, which we can then reference in the CSS file. The ul itself also has an ID and we’ll be using that as well.

Step 7

Thumbnail of screenshot for step 7

Open your CSS file and insert the code found in step7.txt just above your commented-out ‘notes’ block. A detailed explanation follows:

On the first line, we’re giving the ul the same background image we’re applying to its children li elements in order to prevent an image ‘flicker’ effect in Internet Explorer (6 and below).

On the second line, note the use of descendant selectors (more info at http://www.w3.org/TR/REC-CSS2/selector.html#descendant-selectors): all list items that are the children of ul#nav will float left (so the list displays horizontally rather than vertically), and we are taking away any default styles associated with lists (usually a bullet).

The third line sets the properties for any a element that is a child of the aforementioned list item. Those properties are: the height (41px, which is the measurement we learned earlier), block display (needed for image replacement to work across all browsers), absolute positioning (so we can position each one independently of the document structure), and a text-indent of -9999px (which moves the text 9999px to the left (i.e: off the screen)). This method of image image replacement is usually known as the Phark method. Lastly, we have outline:none, which removes the dotted line in Firefox when the link is clicked.

The fourth, separate line declares that all of the buttons specified should have the same background: navBG.jpg.

On the fourth line, why not just declare that with the other ul#nav li declarations?, I hear you ask! You’re right to suggest that, and it’d be a neater way of doing it, but because of the way the cascade is interpreted, ul#nav li is a descendant selector and thus becomes more ‘powerful’ than just declaring an ID. We could modify our forthcoming code to use descendant selectors as well, but that’d end up being even more messy, so listing the IDs here is a much safer, cleaner method.

Step 8

Thumbnail of screenshot for step 8

For the news button, we’ll create three lines of code: one line to set the starting background position and declare the button’s width (and position from the left); a second line to change just the background position on :hover; and a third line to do the same when the button is selected. Use step8.txt as a reference.

Step 9

Thumbnail of screenshot for step 9

Referring to the measurements found in your comments block, use the same format for the rest of the buttons (or replace the entire contents of your CSS file with the code found in step9.txt). The background image is shifted over to the left by the exact amount required to ‘fill’ the space of each button.

Step 10

Thumbnail of screenshot for step 10

To complete the navigation, each page on the site needs to have a unique ID added to the body tag. Using the IDs specified in your completed CSS above, go through each page on your site, modifying the markup to read <body id="pageNews">, <body id="pageShop">, etc. Your finished navigation should now resemble step10.html.

And that’s it! Did you find it helpful?


  1. Sean


    27 October 2007 @ 10:51PM #

    Thanks Elliot!

    Thats just what I needed.

  2. Zinni


    28 October 2007 @ 02:23AM #

    Elliot, do you really code all your sites on a black background like that? very l33t lol. Are there any benefits to this, or just personal preference?

    I guess it does look pretty cool…

  3. Ritchie


    28 October 2007 @ 07:00AM #

    WOW… Very nice.. Using one image for all navigation. _

  4. Carly


    29 October 2007 @ 06:41PM #

    I own that copy of CA magazine, I only realised when I saw the image you created for step 3! Wow, I don’t know whether to follow the tutorial on here or read it in my mag! Either way thanks so much I’ve been wondering how to do this for ages…. :-)

  5. Impulse


    29 October 2007 @ 08:48PM #

    Wow..that’s pretty slick. That is WAY better than using a ton of javascript rollover code.


  6. Elliot Jay Stocks

    Elliot Jay Stocks

    30 October 2007 @ 01:35AM #

    Hey guys – I’m glad you’re finding it useful! Coincidentally, Nick La just posted a very similar tutorial over on Web Designer Wall. It’s worth checking out to hear it being explained in a slightly different way to mine, and it might help solidify any issues you’re unsure of. It’s a beautiful site, too.

  7. Darren Hoyt

    Darren Hoyt

    30 October 2007 @ 03:00AM #

    I’m seeing this solution more and more, and under certain conditions it seems to have as many drawbacks as advantages.

    If you’re building a totally static site that will never grow or change, putting all your nav in one image would work. But sites will inevitably change, and increasingly clients want more control over their pages (via CMS), including the navigation.

    What happens when an extra layout column is needed and the margins between each button need editing – or when new buttons are added. The designer has to redesign and re-export everything with Photoshop until the next time.

    Nick’s example seems somewhat more practical in that the buttons are sliced individually. Adjusting margins with CSS or adding more list-items in HTML is simple, with or without a CMS.

    But with increasing numbers of CMS-driven sites, wouldn’t a more flexible/scalable solution like this one (Sliding Doors/Sprites hybrid) make updates easier in the future?

    I dunno, just another way of looking at it. (And regardless, the Trojan site is fantastic!)

  8. Elliot Jay Stocks

    Elliot Jay Stocks

    30 October 2007 @ 03:19AM #

    @ Darren: You’re not wrong. As with any web design / development technique, this is but one method, and it has both pros and cons. I certainly wouldn’t say this is the be-all-and-end-all of image-based navigation; just one way of getting things done. The example you gave is a great alternative and certainly more scalable, but you pay the price of having browser-rendered text.

    Personally I’ve run into very few problems with the technique above; I would argue that the main navigation of a site should change very little if a site’s structure has been well planned. You could then use less image-dependent methods for more changeable sub-navs.

    But obviously this is in an ideal world! :) Thanks for the kind words, man.

  9. Twisted Barfly [PING]

    Twisted Barfly [PING]

    30 October 2007 @ 05:16AM #

    […] looking back in a magazine today (I wanted to look at the CSS navigation tutorial featured on this site) I found an Illustrator tutorial I could look at, concerning masking.  I wouldn’t have […]

  10. Andrew Ingram

    Andrew Ingram

    01 November 2007 @ 04:26AM #

    A very good and thorough overview.

    It’s a quite long and tedious process but it yields very good results. It’s kind of why I wrote a plugin for Adobe Fireworks that does most of the work for you. I would have made it for Photoshop except that I don’t know of any way to get the guide or slice data from a Photoshop document using the scripting API.

  11. frogx3.com [PING]

    frogx3.com [PING]

    01 November 2007 @ 09:36PM #

    […] Tutoral CSS better nav image replacement | Descargar archivos […]

  12. Mike Tosetto

    Mike Tosetto

    07 November 2007 @ 05:24AM #

    This is a great method for a nav bar. Thanks

  13. designyoutrust.com [PING]

    designyoutrust.com [PING]

    07 November 2007 @ 01:01PM #

    […] Most importantly, it only used one image file; a technique often referred to as ‘CSS sprites’. Read full article. (0 hits) Posted by Dmitry  |  November 7, […]

  14. Killian Tobin

    Killian Tobin

    07 November 2007 @ 11:51PM #

    I love these css sprites even more than I love the tasty soft drink!

  15. Mykal Cave

    Mykal Cave

    16 November 2007 @ 01:53AM #

    Elliot you are awesome. Works like a charm!

    Check it out: michaelrossback.com

  16. Steve


    26 November 2007 @ 03:30AM #

    Hi Elliot. Really love this style of building navigation. I have been trying to use the same method to build a vertical navigation but i am having problems making it work. Could you give me any advice. Thanks and keep up the good work.

  17. RUDE


    02 December 2007 @ 03:32PM #

    Just one little note, removing the outline from the navigation could be a little dangerous if used on institutional sites or similar because you drop keyboard navigation. In fact it could carry legal issues.

    BTW, awesome tutorial, congratulations ;-)

  18. Paul M.

    Paul M.

    03 December 2007 @ 05:44PM #

    Elliot, if you compare your method – one big file, to WebDesigner Wall with every button separate – how would you describe pros/cons? I am just thinking if loading 5 times big image is longer that 5 smaller… Or maybe it does not matter?

  19. Andrew Ingram

    Andrew Ingram

    03 December 2007 @ 06:44PM #

    Paul, using a single image is actually better for two reasons. Firstly, a single image tends to have a smaller filesize than the total filesize of several images. Secondly, having less files means less server requests (a single image is only requested once, regardless of how many times it is used on a page because browsers cache the image after the first download) which is also faster.

    The downside is that having a single image tends to make the css for choosing the right part of the image more complicated.

  20. Paul M.

    Paul M.

    03 December 2007 @ 09:07PM #

    Thanks, Andrew. I wasn’t sure about this, but you clarified my concerns.

  21. Elliot Jay Stocks

    Elliot Jay Stocks

    04 December 2007 @ 12:45AM #

    I’m glad you guys have been finding this useful and thanks for the supportive words. I hope to reprint more of my other magazine tutorials here soon.

    @ Steve: To get it working for a vertical navigation, just do the opposite: make the ‘top’ values change instead of the ‘left’ ones.

    @ Rude: Yeah, you’re right, actually. Well spotted. I’m pretty sure there’s a more accessible technique you can use instead of outline:none, although I can’t remember it right now. Any ideas?

    @ Andrew: Thanks for answering Paul’s question!

  22. John Faulds

    John Faulds

    04 December 2007 @ 05:42PM #

    Another problem with the image replacement method you’ve used (aside from the outline stretching out towards the left when clicked on in Firefox) is that none of the text is viewable with images off because of the negative text-indent.

    I actually wrote something very similar to this last year but used a different IR technique which leaves the text on screen if images are off (the article also covers how to pair the technique with a Suckerfish dropdown so you can ignore the last half if you’re only interested in the single image/nav part).

  23. Ali Sattari

    Ali Sattari

    04 December 2007 @ 08:08PM #

    Just wanted to say a wow for nice grided transparent design.

    And mention this old article, as a related one in Persian(farsi) language: http://weblog.corelist.net/archives/1384/05/08/css-rollover/


  24. Stefan Alexandru

    Stefan Alexandru

    05 December 2007 @ 02:15AM #

    Isn’t exactley what i killed my brains with but it does the trick. Elliot, when i made my css menu i never used text between a tags. Regardin the email i’ve sent you.

    All the best man. Take care.

  25. fatihhayrioglu.com [PING]

    fatihhayrioglu.com [PING]

    05 December 2007 @ 06:50PM #

    […] Güzel bir CSS ile yapılmış resimli menü örneği. Bağlantı […]

  26. teamundead.com [PING]

    teamundead.com [PING]

    10 December 2007 @ 10:22AM #

    […] came across an excellent tutorial for easy image swapping using CSS. I ended up using this technique to do the main nav links for the […]

  27. Tony Johnson

    Tony Johnson

    12 December 2007 @ 10:48PM #

    Great article!

    I’ve used css sprites on a couple projects but with a single image for each link’s state (off, hover, focus). The benefits of using a single image for the entire nav makes sense to me and I’ll use that technique from here on out.

    As for hiding the HTML link text, I recently started placing a span around the actual text (inside the a tag) that simply hides the text. For example:

    span.linktext {

    It works fine in all major browsers.

    Thanks again!

  28. Paul M.

    Paul M.

    13 December 2007 @ 01:02AM #

    The question is, Tony, if from Google perspective (spamming), it is better to use -9999p instead of hidden visibility:hidden or display:none.

  29. Missy


    13 December 2007 @ 03:58AM #

    The problem (forgive me Elliot) is that large sprites take too long to download. I can see your point if the background is complex or if each button is distinct in design (as per your example) but if the nav items all use a similar background then straight up :link and :hover selectors with independent graphics will do just as nicely.

    And I don’t think it would work so well if your Nav is generated dynamically. Forgive the shameless plug but my 10-yr old son’s site is an example (www.car-mad.com). The CSS would need an infinite collection of distinct classes to deal with an ever-changing navigation – hence the decision to KISS.

    The sprite concept is sound, but I would urge anyone out there to weight up the pros and cons first.

  30. userfirstweb.com [PING]

    userfirstweb.com [PING]

    14 December 2007 @ 12:22PM #

    […] Elliot Jay Stocks » CSS Tutorial: better nav image replacement Uses css sprites (tags: css sprites design navigation image replacement) […]

  31. Idetrorce


    15 December 2007 @ 06:13PM #

    very interesting, but I don’t agree with you

  32. Lori


    19 December 2007 @ 07:35AM #

    I enjoyed reading your tutorial and it helped me practice what I read in the book: “The Art & Science of CSS” put out by Sitepoint. The problem I have with the whole method is that it’s not accessible in terms of being able to enlarge the font-size and for the club site I’m trying to redo, there are older folks and some with sight or reading problems who really need to have large fonts. I thought of just making the tabs large, but thought they’d probably look strange if too large and for my “legally” blind friends, I don’t know how big is big enough. I can have him or anyone click a link to increase or decrease all font sizes – when using ems or %. I’m too embarrassed to give the club URI out at the moment. When I update, then I’ll give it out. ; ~)

    Other than that problem, it was a great read and I enjoyed playing with it and then learning my problem before I went too far with it. In the book I mentioned, it told of a whitespace problem in IE6 (of course) and at the bottom IE6 shows a line and then an extra space. To cure it, their remedy to trick it into not putting whitespace around, was on the tag #nav (0r #menu) to add float: left; and then style the tag #nav li {
    margin: 0; padding: 0; float: left; width: 100%; } The float and width removes the whitespace on vertical navigation lists. If anyone was having that problem, hope their solution helps you. It’s a great book!

    Thanks for the terrific read and I’ll be back reading more!

  33. Jermayn Parker

    Jermayn Parker

    20 December 2007 @ 11:28AM #

    Just one question….
    What happens if you enlarge the text, does it get effected??

  34. paula


    09 January 2008 @ 03:52PM #

    Hi Elliot,

    Just want to say big thanks for the tutorial! the navigation works on firefox but not on the latest version of IE/safari. Is there a method to solve this problem?

    thanks a bunch

  35. Elliot Jay Stocks

    Elliot Jay Stocks

    09 January 2008 @ 04:29PM #

    @ Missy: Actually, one large sprite image is smaller in filesize than several individual images combined, which is one of the benefits of this technique. Please see Andrew Ingram’s comment above. However, you’re right about how people should weigh up the pros and cons before deciding on this approach. It goes without saying that this method is unsuitable for dynamically-generated menus.

    @ Jermayn: No, it won’t get effected if you enlarge the text, as specific, pixel-based heights have been declared. This has a downside in that you might want the text size to increase, but if that’s the case, I would advise against using image replacement.

    @ Paula: I’ve just tested it on Safari 3 and IE7, and it appears to be working fine.

  36. ali


    17 January 2008 @ 07:18PM #

    Hi Elliot,

    Great Tutorial, got it working well, however, I was wondering how this could be developed to work on a fixed width, centred design?

    Any tips greatfully received!

  37. benetti.blog.br [PING]

    benetti.blog.br [PING]

    19 January 2008 @ 02:48PM #

    […] Uma informção é necessária saber: neste tutorial é usado o fast CSS rollover para chamadas simples, claro que também é possível otimizar as imagens posteriormente para alcançar pesos condizentes com uma facilidade de transferência e rápida carga da página, combinando imagens para um resultado final. Neste ponto recomento a leitura de Better Nav Image Replacement. […]

  38. bob


    10 February 2008 @ 04:22PM #

    52X2Vl hi great site thx http://peace.com

  39. bob


    20 February 2008 @ 09:08PM #

    mtR26O hi nice site thx http://peace.com

  40. Mitchell Renton

    Mitchell Renton

    27 February 2008 @ 08:44PM #

    Does anybody have any examples of vertical nav? im trying to get one working but have got a bit lost! Numbers aren’t my strong point. iv changed my ‘distance from left’ to distance from top’ and visa versa and iv also changed the width css elements to height and also ‘left’ to ‘top’

    am i missing something else?

  41. resa joee

    resa joee

    08 March 2008 @ 01:12AM #

    can you make me design for my website? contact me via email.

  42. Austin


    26 March 2008 @ 05:16AM #

    Thats very cool!

    I thought it looked familiar, I think I read Nicks first…


  43. Chat


    21 April 2008 @ 10:50PM #

    Very nice website

  44. Searchin


    25 April 2008 @ 03:22AM #

    very nice!

  45. DW


    25 April 2008 @ 04:42AM #

    I’m new to CSS Sprites. It took me a long time to code this out, although the measurements were made easier with Andrew’s Sprite Menu plug-in for Fireworks.

    Very cool, Elliot!! Thanks!!

    I just can’t seem to get the “selected” state to work on any page though. I also tried Andrew’s Sprite Generator…same deal with his version…no “selected” state on any of the pages…either in Firefox or IE7.

    Any ideas?

  46. Home


    25 April 2008 @ 11:18PM #

    very good information, could certainly use the guidance.

  47. Sebastian


    06 May 2008 @ 09:01PM #

    Thanks a lot, your tutorial was really helpful. keeping do it.

  48. Chris


    04 June 2008 @ 09:32PM #

    Hi – Great menu! I am a bit new to CSS and I have having trouble with positioning… In your demo download, the menu will slide with a broswer window resize. (So if I resize my browser from right to left, the menu will slide along with it). How can I make it so the menu sticks in one spot and doesn’t move when I resize the browser window.

    I know it has to do with relative vs. absolute positioning but I can’t figure out where in CSS to edit it. Thx.

  49. Ben


    16 June 2008 @ 11:03PM #

    Amazingly, this doesn’t seem to work on IE6 on XP. Even the demo code menu is shifted up and out of place.

    Am I missing something?

  50. legal


    18 June 2008 @ 08:28PM #

    forget about IE

  51. psula para icat elmas

    psula para icat elmas

    15 July 2008 @ 10:15PM #

    css layer examples / properties and layer attributes

  52. Walter


    20 July 2008 @ 02:41AM #

    I thought I was doing the tutorial wrong because I was getting the flicker effect in IE, but I uploaded the exact files and it was still flickering. Anyone has any suggestions?

© 2005 – 2014 Elliot Jay Stocks. All rights reserved. Powered by Harmony and tracked by Gaug.es. To keep updated with new content, you might like to subscribe to my RSS feed.