Jul 2012

Clever CSS Sprites : Repeating and Non-Repeating

by Steve Wortham

If you're a web designer or front-end developer and you've never created CSS sprites, then it's time to stop living in the past.

Super Mario sprites
Actually, sprites aren't a particularly new idea.  Sprites were first used in video games in 1974.  Basically the idea is that you join a bunch of unrelated images into one big image.  That way, when you're loading your sprites, they're coming from a single bitmap in video memory.  For example, one technique would be to include various positions and poses for a character into a single image such as walking, running, jumping, etc.  And then changing poses would be a matter of shifting the offset of the bitmap, rather than loading a new bitmap entirely.

CSS sprites are a similar concept, but we use them for a different reason.  The reason is simple -- there's overhead and latency in every HTTP request and response.  An HTTP request header is usually going to be over 500 bytes, or perhaps over 1 KB if it contains a cookie.  This is made worse by the fact that bandwidth speeds have improved disproportionately to latency.  In other words, some lucky people have 50 mbps connections to their homes and you'd expect latency numbers to be measured in microseconds by now, but that simply hasn't happened.  Latency is worse on DSL, worse still on mobile connections, and it's even impacted by the distance and network hops involved in the user's connection to your server.  All that said, it's wise to combine files whenever possible, especially when those files tend to be almost as small as the request and response headers themselves.

So how do CSS sprites work, and how can I build them?
Well, in essence it's a matter of creating a block element in your CSS with a specific width and height, and then using the background-position property to position your sprite sheet, as it were.  Rather than write a new tutorial for CSS sprites, I'm going to refer to the best introduction & tutorial for CSS sprites I'm aware of.

Now that you know what CSS sprites are, and how to build them...
I'm going to dive into a slightly more advanced use case for CSS sprites which I implemented yesterday for my site at SilverlightXAP.com.

Yesterday I focused on optimizing the upper-half of the site.  So that means everything you see in green.  There are a few tricky things about this which influenced my strategy.
  1. The big "Royalty-free Silverlight goodness" tagline with the nice gradient is only on the homepage.  So it felt like a bit of a waste to include this in one image along with the header/navigation above it, especially since it's over 16KB.
  2. The green section should stretch across the entire width of the browser, so I'd prefer to use repeat-x in the CSS for this.
  3. The green section of the interior pages of the site is the same color, but is much shorter.

This is what the interior pages look like (notice how much shorter the green section is)...

The slightly unusual part about all of this is building an image where some sprites are going to use no-repeat and others will be using repeat-x.  It turns out that you can pull this off, however, by including the section that'll be repeated horizontally underneath the section that's not repeated.  

So here's what I came up with for header.png...

As you can see, the green area underneath the header is nice and tall, with a 1 pixel horizontal dark line along the bottom. But because most of it is a solid color, the PNG file size is still quite small.  This header.png file is just 11.8 KB.  The other advantage with combining the background with the header is that they'll both load at precisely the same time, which creates a slightly nicer and less jarring experience as the various page elements load.

So I have a header that's 950px wide which uses header.png...

#header {
margin: 0 auto;
padding-top: 25px;
width: 950px;
position: absolute;
top: 0;
background: white url(header.png) no-repeat scroll 0 0;

And then I have a declaration for the body which repeats the lower half of header.png horizontally across the screen...

body {
padding: 0;
margin: 0;
background: white url(header.png) repeat-x scroll 0 -299px;

Notice the background-position is set to -299px vertically.  This is for the interior pages, and it shifts the background up enough to meet up with the lower portion of the header.

On the homepage we then want to override this background-position because we want the green area to be taller...

body {  background-position: 0 -162px !important; }

That takes care of the header. Then silverlight-goodness.png is simple and is only used on the homepage...

And there we have it. The entire upper green section of the homepage is composed of just two images (header.png and silverlight-goodness.png). And for the interior pages it's just a single image (header.png). This front-end optimization along with caching settings, gzip compression, a cookieless domain for static assets, and a CDN makes the site very fast. As always, if you're interested in going beyond CSS sprites and want to optimize your site further, I'd suggest running YSlow to analyze your site and follow its recommendations.