Technology Toolbox

Your technology Sherpa for the Microsoft platform

Jeremy Jameson - Founder and Principal



Using CSS sprites to improve site performance and user experience (a.k.a. Building, part 11)

In my previous post, I briefly mentioned how the Most Popular Posts section on the Technology Toolbox home page uses a CSS sprite to render the arrow image next to each list item.

Technology Toolbox home page
Figure 1: Technology Toolbox home page

If you use Firebug to inspect one of the list items in the Most Popular Posts section, you'll notice the CSS background property is set to:

url("Images/list-item-sprite-1.0.png") no-repeat scroll -200px -137px transparent
You could also use the IE Developer Tools to inspect the element in order to view the corresponding CSS rule, but I prefer the experience in Firebug instead (since, unlike IE, Firebug actually shows you the image in a tooltip when you hover over the URL in the CSS rule).

The "list item sprite" image is actually a 400x400 composite of multiple icons used throughout the site.

Figure 2: list-item-sprite-1.0.png

Notice that the "dashed arrow" icon used in the Most Popular Posts section is actually the fifth image in the set (starting from the lower-left corner). This explains the negative offsets specified in the CSS rule (specifically, -200px -137px).

If you do a quick Internet search for CSS sprites, you will find a number of articles that explain more about the fundamental concepts of CSS sprites and why they are useful for optimizing site perfomance (due to the greatly reduced number of HTTP requests).

There are a few things worth pointing out regarding the use of CSS sprites on the Technology Toolbox site:

  • Why are the images arranged in a diagonal line from the bottom-left corner to the top-right corner?
  • Why is there all that white space between the icons?
  • What happens when someone needs to add a new list item icon?
  • Are there any other benefits to using CSS sprites (i.e. besides reducing HTTP requests)?

Let's tackle these one at a time...

Why are the images arranged in a diagonal line?

If you look at some of the example CSS sprites in the following article...

CSS Sprites: What They Are, Why They’re Cool, and How To Use Them will notice some sites "munge" a bunch of different images (of various sizes) into a single CSS sprite, whereas I have chosen to only combine the "list item" icons into the sample CSS sprite shown in Figure 2.

The reason why I did this is to avoid potential issues when list items span multiple lines. Imagine instead that all of the list item icons shown in Figure 2 were arranged in close proximity (say, for example, aligned vertically with minimal white space between them). In other words, imagine the "minus" icon were directly below the "dashed arrow" icon used in the Most Popular Posts section.

Consequently, in cases where the list item spans two lines (e.g. Upgrade Team Foundation Server 2008 to TFS 2010 (and SharePoint Server 2010)) the "minus" icon would appear below the "dashed arrow" icon (which would definitely confuse users).

Aaron Barker explains more about this technique in the following blog post:

Not all of the CSS sprites used on the Technology Toolbox site use the diagonal layout. For example, the "radio button" images used in the "slider" control (i.e. to the right of the SharePoint Architecture and Development heading in Figure 1) are rendered using a 22x40 sprite. In this case, there's no need to avoid potential issues due to a variable height of the element the CSS rule applies to.

Why is there all that white space between the icons?

If you look at the example sprite shown in Aaron's post, and compare it with the CSS sprite that I created (i.e. Figure 2), you'll notice that I chose to leave a good deal more white space around each icon than Aaron did.

The reason for this is that I prefer to keep the "math" simple, while still allowing for some potentially larger list item icons. Specifically, by treating each list item icon as if it were 50x50 pixels, I know that to show the 5th icon in the set, I need to set the x-position of the background to (5 - 1) * (-50px) = -200px. Figuring out the y-position is slightly trickier, since I may need to take into account the actual height of the icon that I want to show (in order to center the icon vertically with the first line of each list item). In this case, it turns out to be -137px.

To show the "quote" icon, the y-position is 0; to show the "tag" icon, the y-position is -50px; to show the "curved arrow", the y-position is -95px. The point is that I can use a formula to determine the background image positions (perhaps within a few pixels), and then tweak the y-position (as necessary) within in a matter of seconds using Firebug.

Note that the sprite is created by exporting a PNG file from Expression Design. The following screenshot illustrates how I use a "Grid" layer (in a rather hideous shade of orange, so that I remember to hide it before exporting the image) to quickly determine where to place the icons within the overall image. [If you click to enlarge the image, you can probably count the 50x50 pixels used for each cell in the grid -- or you can simply take my word for it :-) ]

List items sprite (Expression Design)
Figure 3: List items sprite (Expression Design)

What happens when someone needs to add a new list item icon?

Ah yes...the astute reader will have realized by now (or perhaps known it a priori) that when I need to add a new icon to the sprite, using this "diagonal layout" method definitely comes with an inherent penalty.

When this happens, I need to fire up Expression Design, expand the artboard (and grid), add the new icon, and then export the updated image -- giving it a new filename to avoid any issues with cached images. This should explain the "1.0" portion in the current filename.

However, by adding the new image in the upper right corner, all of the previously established x and y offsets are no longer valid. Yes, it's a pain -- but keep in mind that the new offsets are very easy to calculate (simply by subtracting an additional 50 pixels -- assuming I've added only one new icon). It's also rather easy to do a search for the sprite filename across the CSS files to determine which background rules need to be updated.

Is this a big deal? least not to me.

Are there any other benefits to using CSS sprites (i.e. besides reducing HTTP requests)?

Well, as a matter of fact, yes. [Otherwise, why whould I have bothered to include this section?]

Take a look at the Archives section displayed on the various blog pages in the site, such as the one shown in Figure 4.

Blog home page
Figure 4: Blog home page

Notice the use of the "plus" icon from the sprite shown in Figure 2. If you click one of the years, the item expands to show the months within that year (for which at least one post was created). As you would imagine, the "plus" icon also changes to a "minus" icon to indicate that clicking the item again will collapse the list.

To accomplish this, I use a little jQuery to toggle the visibility of the nested list items as well as a couple of CSS classes (specifically, expanded and expandable) on the list item that was clicked. These CSS classes determine whether the "plus" or "minus" icon is shown.

If separate image files are used for the "plus" and "minus" icons, users experience a subtle flashing effect the first time they expand one of the years (because the "minus" icon has to be downloaded). By using a CSS sprite instead, the user experience is improved (albeit a slight improvement) because the "minus" icon has already been downloaded. Simply tweaking the x and y positions toggles which icon is displayed.

I'll explain more about the jQuery expandable list used to render the Archives section in a separate post.


No comments posted yet.

Add Comment

Optional, but recommended (especially if you have a Gravatar). Note that your email address will not appear with your comment.
If URL is specified, it will be included as a link with your name.

To prevent spam from being submitted, please select the following fruit: Apple

Please add 8 and 8 and type the answer here: