EJS logo

CSS Tutorial: better nav image replacement

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?