CSS sprites as background

Michał ‘mina86’ Nazarewicz | 6 maja 2013

CSS sprites aren’t anything new. They have been around for years now, and are one of the methods to optimise website load time. The idea is to incorporate several images into a single bigger one and in this way decrease number of round trips between HTTP server and a browser.

In its traditional use, CSS sprites work as a replacement for images and cannot be used as a background. But background is exactly how I’d implemented quote image left of long quotes and flags indicating language paragraph was written in, e.g.:

A few of the entries on my blog have text both in English and Polish. On those, I use some simple icons to indicate which is which:

Polish flag on the left indicates paragraph is written in Polish.

Union Jack on the left indicates paragraph is written in English.

After a bit of playing around I finally figured out how to get this working, and even though there are some caveats, sprites can be used as a top-left no-repeat background image as well.

Basics

Let’s start with the basics. Like I’ve alluded above, sprites incorporate many images into a bigger one and use CSS to display only what is needed. Take the below image with a roll-over image for example.

HTML5

Without sprites it would require 10 separate files but thanks to CSS one is enough. HTML code is rather simple and unimpressive:

<div id="html5"><span
id="spriteh">H</span><span
id="spritet">T</span><span
id="spritem">M</span><span
id="spritel">L</span><span
id="sprite5">5</span></div>

In the absence of styles it simply spell ‘HTML5’ which is excellent. To make an image out of those spans their dimension must be set, text content hidden, and background image added:

#html5 { width: 500px; }
#html5, #html5 span { height: 100px; }

#html5 span {
  display: inline-block;
  text-indent: 100%;
  overflow: hidden;
  background-image: url('html5-sprite.jpg');
}

#spriteh { width:  74px; }
#spritet { width:  52px; }
#spritem { width:  90px; }
#spritel { width:  96px; }
#sprite5 { width: 188px; }

So far it’s nothing more than a standard ‘replace text with a picture’ CSS trick. Because of that, each span would display the image starting with its top-left corner, end result being a rather repetitive image of a letter ‘H’.

To solve this issue browser has to be told to start drawing the image from a different offset — that’s exactly what background-position property does. With a bit more styling, the code is almost complete:

#spritet { background-position:  -74px 0; }
#spritem { background-position: -126px 0; }
#spritel { background-position: -216px 0; }
#sprite5 { background-position: -312px 0; }

The way this works is simple really. In abstract terms, a background image repeats indefinitely in all four directions and its top-left corner is aligned with element’s top-left corner. When rendering the page, browser takes the part of background that overlaps and uses it.

background-position property instructs it to shift the abstract background by given length and only than cut whatever overlaps with the element. By setting position to -126px, the 127th pixel of the background image will be aligned with top-left corner of the element.

With that knowledge, getting roll-over effect is now trivial:

#spriteh:hover { background-position:    0   -110px; }
#spritet:hover { background-position:  -74px -110px; }
#spritem:hover { background-position: -126px -110px; }
#spritel:hover { background-position: -216px -110px; }
#sprite5:hover { background-position: -312px -110px; }

Note that the big image has a ten-pixel gap between sprites. It prevents pixels from adjacent sprites ‘bleeding’ over to the other images.

A bit simpler method

The above method is the ‘normal’ one to use sprites, but in the case of the image presented here, there’s an easier way. Instead of setting background image for each individual span element, it’s simpler to set background for the wrapping div element and cover with span’s only when they are hovered. CSS code for that looks as follows:

#html5, #html5 span { height: 100px; }
#html5 {
  width: 500px;
  background-image: url('html5-sprite.jpg');
}
#html5 span {
  display: inline-block;
  text-indent: 100%;
  overflow: hidden;
}
#spriteh {
  width: 74px;
  background-position: 0 -110px;
}
#spritet {
  width: 52px;
  background-position: -74px -110px;
}
#spritem {
  width: 90px;
  background-position: -126px -110px;
}
#spritel {
  width: 96px;
  background-position: -216px -110px;
}
#sprite5 {
  width: 188px;
  background-position: -312px -110px;
}
#html5 span:hover {
  background-image: url('html5-sprite.jpg');
}

Using sprites for background

Let’s compare the above with a way to implement an icon in a top-left corner of an element:

blockquote {
  background: url('…') no-repeat;
  padding: 0 26px;
  min-height: 20px;
}

The trick here is the no-repeat value which causes the background image to be painted only once. This is contrary to the way sprites work where interesting part of the image is cropped thanks to the limited size of the element. Trying to use an image with multiple sprites as a background would result in whatever is on the right or bottom of the icon to be shown as well.

Here’s the thing though, if there’s nothing on the right or below the icon, if it is the last thing in the sprites image, the above is no longer an issue. This realisation makes it apparent that using a staircase-like image with nothing but transparent background below the diagonal, sprites on the diagonal can be used as a top-left no-repeat background icon.

With that knowledge, the following styles can be constructed:

blockquote, .plFlag, .enFlag {
  background: url('/d/s.png') no-repeat;
}
blockquote {
  background-position: -100px 0;
  min-height: 20px;
}
.plFlag {
  background-position: -50px -50px;
  text-indent: 22px;
}
.enFlag {
  background-position: -75px -25px;
  text-indent: 22px;
}

This technique can be extended in certain situations — either when the height or width of the element is specified. In those situations, more icons can be present either on the right or below the sprite used as a background.

Certainly limited, but if there are only a few cases when this method needs to be used, it allows more images to be converted into sprites than previously expected. And it’s what enabled me to convert all but one small images on this site to CSS sprites.