Hi there, my name is Robby Macdonell, I'm a user experience designer and sometimes front-end developer. I live in Seattle, WA but grew up in The South (and spent time in Alaska and Massachussetts in between).

(sorta) Practical CSS sprites for high-traffic sites:

This past week, I learned about how google is starting to take server speed into account in their search result rankings.

It reminded me of some work I did a year or so ago when I was a front-end developer for Jobster.com. We were getting just shy of a million unique visitors / month at the time, and our servers were starting to strain under the load. We solved some pretty big performance problems by reducing 50+ image requests on each page to just 2 using CSS sprites. Here's some of the stuff I learned during that process.

Disclaimer: In most situations, bending over backwards to get everything into a single sprite is overkill, and I don't really recommend it unless you're suffering from high-traffic performance problems. If you find yourself in that spot, however, it can make a huge difference.

First off, look at these two pages, for a fictitious online candy retailer called BaddassSnacks! They should look identical. The only differences are under the hood. I noted some details about the assets used in each page.

Demo #1

Demo #2

Total image files: 24

no-sprites-image-files.gif

Total image files: 1

sprites-image-files.gif
click here to see sprite.gif

Getting all of these images to work with a single CSS sprite was a bit of a challenge. Here's a few of the techniques I used to make it all work:

1. Replacing a block of text with an image:

This is one of the most straightforward things to do with CSS sprites. Just make sure the text is contained in a block-level element and set the height and width of the element to the same as the image. Of course, you'll need to hide the text too, here's how to do it.

/* page logo, for example */
H1#logo A {
  display: block;
  height: 100px;
  width: 175px;
  text-indent: -9000px;
  outline: none;
  /* set the background to your sprite, then move the sprite into position
     in this case, 25px from the left and 50px from the top */
  background: url(/path/to/your/sprite) -25px -50px no-repeat;
}

This can turn into a lot of CSS if you're doing this with a lot of images. You can clean this up a bit by creating a single class that you can apply to any text-containing element that you want to turn into an image. I usually call this class ".img" or something similar.

.img {
  display: block;
  text-indent: -9000px;
  outline: none;
  background: url(/path/to/your/sprite);
  background-repeat: no-repeat;
}

H1#logo A.img {
  height: 100px;
  width: 175px;
  background-position: -25px -50px;
}

here's an example from the demo page:

sprites-replaced-text.jpg

2. long, tiling backgrounds

If you need an image to tile horizontally AND vertically, you're out of luck when it comes to sprites.
But if you can live with only tiling images horizontally, then you just make a REALLY tall sprite with all your horizontally-tiling images, then use background-position to line them up to the right point.

Again, an example:

sprites-horizontal-tile.jpg

3. Backgrounds for elements with a variable width

This is really similar to #2
You can use the sliding doors technique with CSS sprites, you just need to make sure that your sprite is as wide as your widest variable-width element is likely to be.

sprites-sliding-doors.jpg

4. Bullet icons

This is where things start to get a little bit tricky when you're using sprites, and it's difficult to come up with something that's bulletproof. The problem is, in a nutshell, that you want to make use of individual icons, but because they're on the sprite, the neighbors of the image you want will show through unless you've positioned them just so. In this example, I just made the image really tall and gave everything a lot of padding. I've found that increasing the dimensions of the image doesn't increase the file size proportionally, so you can get away with it and still achieve a smaller total file size.

sprites-icons.jpg

5. Inline images

These seem tricky, but are actually quite easy, so long as you're willing to put up with one teeny bit of ugliness in your code. The obvious problem is that inline elements can't reliably get a height and a width in all browsers. You need something that will act like an IMG element. As it turns out, that's what you use. But, we want to keep our images all in the sprite, so how do we do it? There are two ways. First, and easiest, accept the extra server hit and serve your inline images as a 1x1px transparent gif.

<img src="/images/invisible.gif"  />

Then use css to set the height and width of the image and use your sprite the same way as you would with any block-level element. That's what we did on Jobster.com, and it worked fine. The second way is uglier, but will keep you from using a second image. The SRC attribute of the IMG tag usually takes a url to an image file, but you can also put raw image data in there. This will give you an image that's the equivalent of the 1x1px transparent gif:

<img src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="  />

Did I mention it's ugly? In either case, you can then use CSS to position the sprite like so:

IMG#whatever_id_you_give_to_the_inline_image {
  height: 15px;
  width: 45px;
  background: url(/path/to/your/css/sprite) no-repeat -50px -65px
}

sprites-inline.jpg

And that's basically it. For a real-world example of most of these techniques, check out the search results pages on Jobster.com (although I don't work on that site anymore so no guarantees that things haven't changed in the meantime)