CSS Sprite Management with Gulp
Front-End Full-Stack WebCSS sprites provide the ability to pack multiple small images into a single file, which is then used as a background on multiple HTML...
Previously on CSS Sprites with Gulp, we learned how to automatically generate a sprite sheet and accompanying CSS from a folder of images. That’s a great first step, but there’s a lot more we can do.
In the previous code examples, you may have noticed that the Gulp tasks were set up to load only PNG images from the img/sprites
folder.
However, you absolutely are not limited to PNG formats. By changing the image paths from *.png
to simply *
, Spritesmith will load every file in that folder—so be sure you don’t have any images around that you wouldn’t want added to the sprite sheet.
You should also aware that if you have any icons that need to have alpha-transparent backgrounds, you’ll still need to save the outputted sprite sheet as a PNG file to preserve them. If you saved it as a JPG, for instance, all the transparent bits would render as black.
In reading the first installment of this article, I’m sure many were wondering, “What about SASS output?”
While we’re already rendering really useful CSS for our sprite sheet, we can unlock a lot more usefulness by generating SASS instead.
To get started, we’ll need to install the gulp-sass
plugin:
npm install --save-dev gulp-sass
And add it as a dependency in our gulpfile.js
file:
var sass = require('gulp-sass');
Now, all we have to do for SASS output is specify a filename in our sprite
task with an scss
extension, like sprite.scss
:
gulp.task('sprite', function () {
var spriteData = gulp.src('img/sprites/*')
.pipe(spritesmith({
imgName: '../assets/img/sprite.png',
cssName: 'sprite.scss' // <<-- just need to change this line
}));
spriteData.img.pipe(gulp.dest('img'));
spriteData.css.pipe(gulp.dest('css'));
});
Now run the sprite
task:
gulp sprite
And a new sprite.scss
file should be generated. Let’s also add a new watcher to the watch
task that will listen for changes to SASS files and automatically run the sass
task.
gulp.task('watch', function () {
gulp.watch(['img/sprites/**/*'], ['sprite']);
gulp.watch(['css/**/*.scss'], ['sass']);
});
Depending on how many sprite images you have, the generated sprite.scss
file can get quite long, so let’s break it down a bit.
The bulk of the SASS sprite sheet file are variables whose names correspond to their source image counterparts. Let’s look at this example, based on a hypothetical banner1.png
image:
$banner1-name: 'banner1';
$banner1-x: 250px;
$banner1-y: 0px;
$banner1-offset-x: -250px;
$banner1-offset-y: 0px;
$banner1-width: 820px;
$banner1-height: 60px;
$banner1-total-width: 1070px;
$banner1-total-height: 250px;
$banner1-image: '../assets/img/sprite.png';
$banner1: (250px, 0px, -250px, 0px, 820px, 60px, 1070px, 250px, '../assets/img/sprite.png', 'banner1', );
These variables should be fairly straightforward and include things like the sprite’s dimensions, position within the master sprite sheet, URL, etc. The same variables have been recreated for every other file within the img/sprites
folder.
At the end of the file are variables for the overall sprite sheet:
$spritesheet-width: 1070px;
$spritesheet-height: 250px;
$spritesheet-image: '../assets/img/sprite.png';
$spritesheet-sprites: ($banner1, $banner2, $button1, $button2, $button3, $icon1, $icon2, $icon3, $photo1, );
$spritesheet: (1070px, 250px, '../assets/img/sprite.png', $spritesheet-sprites, );
Next are a series of SASS mixins used to add sprite information to your other CSS selectors. Let’s go over them one by one. It should be pointed out that your sprite.scss
file should be included by another SASS file, and not manipulated by itself, as it will update every time you rebuild the sprite sheet.
@import 'sprite';
This mixin is used to output the width of a given sprite image. The sprite-height
mixin is the same syntax.
@include sprite-width('banner1.png');
This is used to output the exact offset (coordinates) of a given sprite:
@include sprite-position('banner1.png');
Outputs:
background-position: -250px 0px;
This outputs the background image url:
@include sprite-image('banner1.png');
Outputs:
background-image: url(../assets/img/sprite.png);
All the above mixins may not seem immediately useful, but they can come in handy in certain circumstances. Here are the really useful ones:
This will output a fully formed CSS selector for the specified sprite.
@include sprite('banner1.png');
Outputs:
background-image: url(../assets/img/sprite.png);
background-position: -250px 0px;
width: 820px;
height: 60px;
This is the big kahuna mixin that will loop over all your available sprite image filenames and generate all the associated CSS. To insert it into your CSS, simply call:
@import 'sprite'; // imports sprite.scss
// $spritesheet-sprites is an array of all sprite image names
@include sprites($spritesheet-sprites);
From there, all you have to do is specify the class names in your HTML:
<div class="banner1"></div>
What if you don’t like the SASS that’s output by Spritesmith? Good news: you can change it!
Deep inside node_modules/gulp.spritesmith/node_modules/spritesheet-templates/lib/templates
, you should see a file called scss.template.handlebars
. This template is used to lay out the actual SASS output of sprite.scss
. To use it to modify it to your liking, just make a copy of that file and put it in your css
folder (or wherever). Then, add this config option to your sprite
task in gulpfile.js
:
cssTemplate: 'css/scss.template.handlebars' // using whatever file location you used
Then just make whatever modifications you need in that template. For example, when I first started using SASS with Spritesmith, I didn’t like that the default selectors specified an exact width and height for sprites. Often, you want to attach a sprite to the upper left corner of a container, but allow the container to be larger. An example would be a wide button that contains HTML text, and a small icon on the side. There’s no reason to create an icon that extends to that full width, so just make it small and anchor it to the side.
So, I duplicated the sprite
mixin and called the new one sprite-box
. This is now the mixin that I’ll call for a fixed width and height sprite. In the original mixin, I’ll simply remove the references to dimensions. So now those two mixins in the Handlebars template look like this:
@mixin sprite-box($sprite) {
@include sprite-image($sprite);
@include sprite-position($sprite);
@include sprite-width($sprite);
@include sprite-height($sprite);
background-repeat: no-repeat;
}
@mixin sprite($sprite) {
@include sprite-image($sprite);
@include sprite-position($sprite);
background-repeat: no-repeat;
}
Note that I also added no-repeat
on both, as I rarely have a need for sprite backgrounds to repeat.
So now, all our sprite classes are output in the CSS without dimensions, like so:
.banner1 {
background-image: url(../assets/img/sprite.png);
background-position: -250px 0px;
background-repeat: no-repeat;
}
And in any case where it’s necessary to specify dimensions, just call the sprite-box
mixin:
header {
@include sprite-box($banner1);
}
Which will output this CSS:
header {
background-image: url(../assets/img/sprite.png);
background-position: -250px 0px;
width: 820px;
height: 60px; }
In the case above, we used the $banner1
variable as an argument instead of 'banner1'
to send the entire array of sprite data to the mixin.
So far, we’ve only briefly touched on some of the configuration options you can give to Spritesmith, including:
There are a lot more to choose from, a couple of which I’ll go over briefly:
Most of the time, you won’t want the images in your sprite sheet touching each other. It’s good to give them adequate breathing room so that their neighboring sprites aren’t accidentally seen. By setting the padding
option, you can specify the amount of pixel padding around each image:
padding: 50
This is perhaps one of the most important options, but one you’ll just have to play around with to find one that suits your layout. The algorithm basically tells Spritesmith how to arrange individual sprites within the sheet. There are five options:
The default is binary-tree
, which will stack the images in the most efficient way possible, horizontally and vertically. diagonal
and alt-diagonal
arrange them, you guessed it, diagonally. This is a good option to use when it’s critical that sprites aren’t directly above, below or beside each other. One caveat to using diagonal layouts is that your sprite sheet can get very wide quickly. Mobile Safari (and perhaps other browsers) will actually break on backgrounds larger than 3mb. They just won’t display at all at about 6,000px in my tests—I learned this the hard way.
In the end, I prefer using the top-down
option, which lays out each image in a vertical line, along with ample padding between them (usually around 50px). So far, I’ve been able to keep sprites from overlapping in containers, while staying within manageable dimensions.
Hopefully, these tutorials have been helpful for devs looking for a better way to manage CSS sprites. There are lots of other things you can add on to this technique, like tasks that optimize your sprite sheet for smaller files sizes when it’s created, or support for retina-size images. One thing’s for sure: Once you’ve started generating your sprite sheets automatically, you’ll never go back to the old way.
CSS sprites provide the ability to pack multiple small images into a single file, which is then used as a background on multiple HTML...
I’m a front-end developer, which means I write HTML, CSS and JavaScript code all day. But I also frequently work with back-end developers on...
Large, full-screen photos seem to be everywhere on the web. But while photos are getting bigger, so are file sizes. Image optimization with Adobe...