fbpx

Blogs from the Ranch

< Back to Our Blog

CSS Sprite Management with Gulp, Part 2

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.

Multiple formats

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.

SASS

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.

A Look Inside

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, );

Mixin It Up

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';

@mixin sprite-width($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');

@mixin sprite-position($sprite)

This is used to output the exact offset (coordinates) of a given sprite:

@include sprite-position('banner1.png');

Outputs:

background-position: -250px 0px;

@mixin sprite-image($sprite)

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:

@mixin sprite($sprite)

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;

@mixin sprites($sprites)

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>

Change It Up

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.

Configuration

So far, we’ve only briefly touched on some of the configuration options you can give to Spritesmith, including:

  • imgName
  • cssName
  • cssTemplate

There are a lot more to choose from, a couple of which I’ll go over briefly:

padding

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

algorithm

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:

Sprite layouts

  1. top-down
  2. left-right
  3. diagonal
  4. alt-diagonal
  5. binary-tree

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.

Wrapping Up

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.

Not Happy with Your Current App, or Digital Product?

Submit your event

Let's Discuss Your Project

Let's Discuss Your Project