Compass and Slicy Will Make Your Sprites and Write Your CSS

Making sprites sucks. They’re incredibly useful for web development since they keep assets minimal and HTTP requests (and thus loading times) low, but they are a huge time sink: somebody has to Tetris together a bunch of tiny images into one file so that they fit in as little space as possible, then figure out all the dimensions and coordinates of every little icon, make up class names for every single one, then write a bunch of verbose CSS so you can actually use the individual icons in the sprite. What a pain.

I used to argue with our designers about who should build sprites. But even if I escaped the burden of creating one, writing the related CSS was painful too. Adding a new icon to a sprite was regularly a source of mental anguish. Our main sprites can easily have over 50 different images and icons in it — when you run a huge website like the Behance Network or a complex web application like Action Method, it’s not unusual to be dealing with that many different icons. So we changed our spriting process completely.

The red arrows are pointing to icons used in AMO. This is only a fraction of the icons in the sprite.

Now, we let two applications do the hard work for us: Slicy (previously known as Layer Cake), a Mac application by MacRabbit that automatically slices and exports your Photoshop layers based on their names, and Compass, a popular Sass framework with a built-in sprite generator.

Our work flow is brilliantly simple and quick now. Our designers name their layers as they go along, then simply drop their PSD into Slicy, which generates individual images based on the layer names. The generated images are then handed off to the developer, and the designer’s contribution to the sprite is now completely done. (Seriously.)

The layers with .png appended so Slicy knows what to export.

For the developer, the process is just as simple thanks to Compass. All it takes is a little bit of magic: in a Sass file, tell Compass where your individual images are, set a couple customization variables, save your code and let Compass stitch all of your images into one sprite. Along with your new sprite, Compass generates the CSS for every individual image’s class name, dimensions and coordinates. The only thing left is to put class names in your markup. I kid you not. It’s that easy.

The individual images, the compiled sprite, and the generated CSS.

Here’s the technical breakdown for getting Compass to generate a sprite:

1. Our file structure looks like this:

config.rb
css/
  sprites.css
images/
  sprite/
    dashed-border-add-file.png
    icon-actionstep-focus.png
    icon-actionstep-overdue.png
    icon-actionstep-today.png
    icon-activity-accept-selected.png
    ... all the rest of the individual images Slicy made for us ...
sass/
  sprites.scss



2. The config.rb for our Compass project designates where our Sass, CSS and images are stored. We also have a hook for automatically renaming and moving sprites when they’re done being compiled.

http_path = "/" # not used in this exercise
css_dir = "css"
sass_dir = "sass"
images_dir = "images"
javascripts_dir = "javascripts" # not used in this exercise

output_style = :expanded
sass_options = {:unix_newlines=>true}
line_comments = false
preferred_syntax = :scss

# Rename sprites to remove the Compass-generated hash and move it up 1 directory
on_sprite_saved do |filename|
  if File.exists?(filename)
    FileUtils.mv filename, filename.gsub(%r{-s[a-z0-9]{10}\.png$}, '.png').gsub('images/../images/', '')
  end
end



3. In sprites.scss, we set some magic variables and call some magic functions – they’re magic because their existence is based on the name of the folder where your images are stored. Our images are stored in a folder called “sprite” (see our directory structure above).

$sprite-sprite-base-class: '.sprite';
$sprite-layout: smart;
$sprite-sprite-dimensions: true;

@import "compass/utilities/sprites/base";

@import "../images/sprite/*.png";
@include all-sprite-sprites;



A quick breakdown of the few options we set:

  • $sprite-sprite-base-class: Tells Compass to define one additional class with the sprite as its background-image. Use as $<folder name>-sprite-base-class.
  • $sprite-layout: This tells Compass how we want our images laid out within the sprite. The options are vertical, horizontal, diagonal and smart. Smart means that it packs all the images in together as tight as possible so that the footprint of the final sprite is as small as possible. Use as $<folder name>-layout.
  • $sprite-image-dimensions: Tells Compass to include the dimensions of each individual image in the CSS along with its coordinates. Use as $<folder name>-image-dimensions.



Note that there are many other customizations you can set for your sprite, but these 3 did the job for us. For the full list of options, check out the documentation at http://compass-style.org/help/tutorials/spriting/customization-options/.

The final step is to @import the Compass sprite module and all of the images we want compiled into a sprite. Then, by calling the magic function all-sprite-sprites (all-<folder name>-sprites), Compass will dig into our sprite folder, stitch everything together, drop a CSS file into our css folder and a complete sprite into our images folder. The result is:

  1. A complete sprite.

  2. A complete CSS file. Ours is big (look at all those icons!), so here’s a sample:
    .sprite, .sprite-dashed-border-add-file, .sprite-icon-actionstep-focus, .sprite-icon-actionstep-overdue, .sprite-icon-actionstep-today {
      background: url('/images/../images/sprite-s134b848ff6.png') no-repeat;
    }
    
    .sprite-dashed-border-add-file {
      background-position: 0 -177px;
      height: 145px;
      width: 342px;
    }
    
    .sprite-icon-actionstep-focus {
      background-position: -293px -64px;
      height: 27px;
      width: 24px;
    }
    
    .sprite-icon-actionstep-overdue {
      background-position: -269px -64px;
      height: 27px;
      width: 24px;
    }
    
    .sprite-icon-actionstep-today {
      background-position: -245px -64px;
      height: 27px;
      width: 24px;
    }
    



At this point, it’s important to note that obviously the path to your image is wrong if you’ve kept our config.rb function to rename and move the file. However, I’m totally okay with this, because I don’t love that first rule anyhow (too many selectors for my taste), so the first thing I do after compiling a sprite is change it to be simpler and more useful:

.sprite {
  display: inline-block;
  background: url('../sprite.png');
}

Now, for any element that should be an icon, I just apply two classes: sprite and whichever class references the icon I’m looking for. If you ever need to add any more icons to your sprite, just drop the images into your folder and let the sprite re-compile. All the existing class names stay the same, so any new background positions are automatically reflected. You don’t have to change any of your markup.

Spriting has never been more blissful.

If you have any questions about anything in this blog post, or you just want to talk about Sass and Compass, leave a comment below or follow me on Twitter at @jackiebackwards.