CSS sprites provide the ability to pack multiple small images into a single file, which is then used as a background on multiple HTML elements. By simply defining the dimensions of the container and shifting the background position around, you’re able to achieve image-based styling globally with just one HTTP request. For larger sites, particularly e-commerce, this technique can reduce those requests by the dozens.
At first, I was dismissive of this method, due to a few key drawbacks. For one, being background images, there’s no guarantee they would be visible on printouts. And they could easily break if a user increased the zoom level in their browser. Plus, improper use could adversely affect accessibility.
However, as browsers evolved, I saw the light and have been making use of sprites for things like icons and navigation for years.
Not So Fast: Maintenance Concerns
Enter the real problem with CSS sprites: maintenance. As useful and performant as this technique is, it can be a challenge to create and update even the smallest sprite sheet. If you’re starting from a layered Photoshop design, you first have to extract all “spriteable” elements and place them into a new layered file. Then you’ve got to position them all so that they have ample space between them. Next, export them as a single file, often an alpha-transparent PNG. Then assign classes to your HTML containers and attach the sprite as a background, shifting around coordinates until it looks right. This can often require nudging pixels around in your browser’s web inspector. Good times.
And then… one of those images will have to change. You’ll have to either go back to the original PSD and repeat the process, or update the layered sprite file (if you’re lucky enough to have saved it unflattened). Then you’ll export the PNG again. Rinse, repeat.
But what if the size of the icon has to change? Now you’re in trouble. What if the new image takes up double the space within the sprite sheet? You’ll have to move other icons out of the way. You make the changes and export the PNG, but now you’ve completely blown up your CSS backgrounds! All the dimensions and coordinates are now way off, so you go back and manually adjust everything and hope you never have to do this again.
But you will. Many times.
And it’s even worse when you’ve inherited a large sprite sheet, but don’t have access to the original source files.
This is where an automated sprite generator proves invaluable, and I’ll show you how to implement one using Gulp, a popular task runner.
Installation
This sprite management technique requires installation of Node, Gulp and a couple of plugins.
A full tutorial on installing Node is beyond the scope of this article, but you can find ample instructions via Nodejs.org.
Once Node is installed, you can now install Gulp fairly easily via the command line:
npm install -g gulp
npm install --save-dev gulp
One Gulp plugin we’ll need to install is gulp.spritesmith. Do so via this command:
npm install --save-dev gulp.spritesmith
This will create a folder called node_modules
with the Spritesmith plugin underneath.
Now create a file named gulpfile.js
in the root of your project folder with this starting code:
var gulp = require('gulp');
var spritesmith = require('gulp.spritesmith');
// Gulp requires a default task to be present
gulp.task('default');
Let’s Get Started
What good is a sprite sheet without images, you say? I’ve created eight placeholder image files inside the /assets/img/sprites folder of the project to get us started.
The first thing we need to do is set up a Gulp task to gather all images in /assets/img/sprites
, combine into a single sprite sheet and generate corresponding CSS styles.
gulp.task('sprite', function () {
var spriteData = gulp.src('img/sprites/*.png')
.pipe(spritesmith({
/* this whole image path is used in css background declarations */
imgName: '../assets/img/sprite.png',
cssName: 'sprite.css'
}));
spriteData.img.pipe(gulp.dest('img'));
spriteData.css.pipe(gulp.dest('css'));
});
To break it down, here’s what’s happening in this task:
- Gulp gathers data for each PNG image within the
/assets/img/sprites
folder.
- We define the name of the generated sprite sheet.
- And define the name of the generated CSS file.
- We then save the sprite sheet to the destination folder: img.
- And save the CSS file to the destination folder: css.
Let’s try it out. Start Gulp, and then run the sprite
task:
If all went well, you should see a new file appear at img/sprite.png and css/sprite.css. Here’s what the sprite file looks like at this point:
As you can see, the images are contained in one file, all butted up against each other. As is, it’d be pretty difficult to determine each sprite’s exact dimensions.
Here’s the magic, though. Look inside sprite.css and see that all the dirty work has been done for you. For each image, a CSS class selector has been defined to specify the dimensions and position of the background image. The class is named identically to the image filename:
.icon-banner1 { /* corresponds to the file named banner1.png */
background-image: url(../assets/img/sprite.png);
background-position: 0px 0px;
width: 820px;
height: 60px;
}
Things Change
We’re good to go, right? Just then, your designer changes the height of the top banner image. No problem!
You just re-slice the banner from the PSD and save it over the old banner1.png
file. Now run:
And the sprite sheet is regenerated, as is the CSS file with the new information. All the dimensions and coordinates are updated to reflect the modified banner image. And best of all? None of your HTML needs to change. Since all the class names are the same, everything just updates.
A Little Automation
We could probably stop right there and would still be saving ourselves tons of time and frustration. But wouldn’t it be great if we could rebuild the sprite sheet automatically, simply by dropping a new file in the /assets/img
folder?
Gulp Watchers
This is where another plugin comes in handy. To get it set up, we’ll need to install Gulp-Watch:
npm install --save-dev gulp-watch
Then, add the dependency to gulpfile.js
:
var watch = require('gulp-watch');
Now we add a watch task:
// Watch for changes to sprite images
gulp.task('watch', function () {
gulp.watch(['img/sprites/**/*.png'], ['sprite']);
});
All we’re telling Gulp to do is watch for any additions or modifications to PNG files in the sprites folder, and then run the sprite
task as necessary.
Now, tell Gulp to launch the watch
task when it’s first run.
gulp.task('default', ['watch']);
Restart Gulp, and it should now continually listen for anything that happens in the sprite image folder. As before, it’ll regenerate the sprite sheet and all CSS selectors as needed.
But Wait, There’s More!
Let’s look at what we have so far:
- A master sprite sheet is auto-generated from a folder of images.
- A CSS file is created with all the individual sprite data, with class names tied to the image filenames.
- A watcher function listens for changes to that folder and regenerates everything as needed.
This alone will save you precious time and headache. But there’s a lot more we can do. Watch for part two of this article, where I’ll show you:
- How to generate Sass files instead of CSS, complete with loads of useful variables.
- How to use templates to modify the CSS/Sass output to meet your exact needs.
- How to use some of the more useful Spritesmith configuration options.
Andrew will be at CSS Dev Conf, Oct 26-28. Be sure to say hello!