How to automatically generate CSS sprites with Sass and Compass

In this tutorial you will learn how to create CSS sprites automatically, using Sass and Compass! No more going back and forth into Photoshop and figuring out the position of each image, then adjusting your stylesheet manually. This tutorial is aimed at Windows users, but the principles are all the same.

If you want a more fine tuned result when creating CSS sprites, check out my other tutorial for a different method of automatically generating advanced CSS sprites with Sass and Compass.

For this tutorial, I’ll presume you have Sass and Compass running inside your website development environment. If you don’t, learn how to install Sass and Compass in a previous article here.

Table of contents:

Setup your images/icons

Firstly, start by organising your individual images that make up the sprite, into a directory. As default, when the sprite is created, the file names are using as part of the CSS selector, so ensure that the file names are valid CSS selector naming conventions. E.g. a selector must not start with a number (not valid; 60x40px-bubble). If you want more control over the selector names, which can avoid issues like this, see further down in this tutorial. Anyway, here are mine:

sprite-images

Make sure you’ve told compass to watch your Sass/CSS directory for changes, then open up your Scss file containing your styles so far.

Inside the .scss file, you need to import the CSS sprite utilities that Compass provides. Type; @import "compass/utilities/sprites":

import-css-sprite-utilities-compass

Before the next part, you need to have set up an image directory. If you haven’t already done this, open the config.rb file inside the Compass project, and add images_dir = "../img" or similar (where ever your images are located):

images-directory-compass

For me, my images directory is called img and is one directory up from my Compass project. If you’ve made a change to the config file, you will need to restart the Compass watch.

Next, we import the the images into our stylesheet. We use the following command to target all pngs inside of our designated folder; @import "grey-icons/*.png"; And also add the include command; @include all-grey-icons-sprites; Like so:

compass-import-sprite-images

Now simply save this file! And watch Compass automatically generate your sprite:

compass-creates-sprite-automatically

If you now open your complied CSS file, for me, that’s screen.css, you’ll see that the CSS has automatically been generated, to include all the positioning etc for you:

auto-css-declarations-for-sprite-positioning

Here it is:

sprite-created

So inside your HTML, you’ll need to add the specific positioning class, e.g. grey-icons-bubble to each sprite element, and you’re done!

NOTE: If you don’t want the random string of numbers and letters appended to the sprite image filename, see this section later in the article.

You may notice that the image path goes /../img for the background URL, and you may find this doesn’t match up with your structure. If that’s the case, open up the config.rb file, and add in this setting; http_generated_images_path = "/assets/img" or similar, depending on your choice of directory. Like so:

sprite-generated-background-url-config

You’ll have to restart the compass watch for the project directory for the configurations to take affect, after which you’ll be generating your custom URL paths for your sprites.

More control over the automatically generated CSS selector names

You may not approve of the conventions that Compass automatically provide for your CSS selector names. And that’s okay, because you can adjust these settings quite easily.

If you go back inside of the screen.scss file, and add the following:

custom-css-sprite-selector-names

The part in parenthesis needs to be your file name; @include grey-icons-sprite(bubble). Just follow the pattern above to do it for your own custom options.

Save the above file to trigger the auto-compile, sprite and CSS generation! Then open your CSS file and notice your custom selectors:

custom-css-sprite-selector-names-with-compass

Change the padding between the images

If you require more control over the padding between the CSS sprite, you need to override the spacing configurable variable. For the above example, we type; $grey-icons-spacing: 10px; for 10px padding between the images. Like so:

auto-sprite-padding

Save the file, and then locate to the sprite output. You’ll notice the space between the images now:

sprite-images-with-spacing

Include the dimensions of each sprite

If you need to know the dimensions of each sprite, perhaps you are making it a block element to be the size of the image, then use the sprite-dimensions variable. By default it’s false, so type; $grey-icons-sprite-dimensions: true; Like so:

include-sprite-dimensions

Save this and then check your CSS file, and you’ll notice the dimensions have now been included for each icon. For me, they’re all 60x60px so it’s not a great example, but you’ll get the idea:

include-sprite-dimensions-css

To read the official documentation on everything here, you can visit the Compass website here for spriting.

Remove the random string in the sprite filename

If you don’t want the random string that’s appended to the filename when a new sprite is created, here’s how to remove it. You will need to open config.rb inside your Compass project, and add the following code:

# Make a copy of sprites with a name that has no uniqueness of the hash.
on_sprite_saved do |filename|
  if File.exists?(filename)
    FileUtils.cp filename, filename.gsub(%r{-s[a-z0-9]{10}\.png$}, '.png')
    # Note: Compass outputs both with and without random hash images.
    # To not keep the one with hash, add: (Thanks to RaphaelDDL for this)
    FileUtils.rm_rf(filename)
  end
end

# Replace in stylesheets generated references to sprites
# by their counterparts without the hash uniqueness.
on_stylesheet_saved do |filename|
  if File.exists?(filename)
    css = File.read filename
    File.open(filename, 'w+') do |f|
      f << css.gsub(%r{-s[a-z0-9]{10}\.png}, '.png')
    end
  end
end

This will output both the hash version and a version without the hash. And the CSS stylesheet references the non-hashed version. You can read more about this here.

Remove the hash string in the sprite filename and replace with a query string

Similar to the above solution, where we retain the same filename and remove the hash string, we can also retain the same filename and append a query string to the end of the filename for another cache busting solution (thanks to RaphaelDDL in the comments for supplying this alternative). Here’s the code (it’s almost identical to the above code snippet to remove the hash, apart from one line):

# Make a copy of sprites with a name that has no uniqueness of the hash.
on_sprite_saved do |filename|
  if File.exists?(filename)
    FileUtils.cp filename, filename.gsub(%r{-s[a-z0-9]{10}\.png$}, '.png')
    # Note: Compass outputs both with and without random hash images.
    # To not keep the one with hash, add: (Thanks to RaphaelDDL for this)
    FileUtils.rm_rf(filename)
  end
end

# Replace in stylesheets generated references to sprites
# by their counterparts without the hash uniqueness.
on_stylesheet_saved do |filename|
  if File.exists?(filename)
    css = File.read filename
    File.open(filename, 'w+') do |f|
      f << css.gsub(%r{(?<start>-s)(?<hash>[a-z0-9]{10})(?<file>\.png)}, '.png?v=\k<hash>')
    end
  end
end

This will produce CSS similar to the following:

background-image: url('../img/ico.png?v=ab47cfd764');

As you’ll be able to see, it adds a query string to the filename for uniqueness, offering another cache busting alternative.

20 thoughts on “How to automatically generate CSS sprites with Sass and Compass

  1. RaphaelDDL

    Heya Peter :)

    While sometimes having problem with browser cache due to same name, I came up with a better way of keeping the file with same name while still maintaining the hash for cache purposes and wanted to share with ya, since you helped me alot with the tutorial :D

    Change:
    f << css.gsub(%r{-s[a-z0-9]{10}\.png}, '.png')

    For:
    f << css.gsub(%r{(?-s)(?[a-z0-9]{10})(?\.png)}, ‘.png?v=\k’)

    This way instead of `background-image: url(‘../img/ico-sab47cfd764.png’);`, it will be `background-image: url(‘../img/ico.png?v=ab47cfd764′);` (without `-s` and `.png`).
    This way helps browser get a new version instead of cache (because of same file name).

    Hope helps someone else ;)

    Best Regards,
    RaphaelDDL

    Reply
    1. Peter J Langley Post author

      Hello RaphaelDDL =)

      Sorry for my late response (I was on holiday)! Thanks so much for coming back here and offering this alternative solution for cache busting – I’m very grateful. I have added it to the end of this article, so please do let me know if I missed anything.

      Thank you again =)

      Reply
      1. RaphaelDDL

        Hi there Peter,

        You helped me alot with this post so no worries, glad to be of help! I’m still a noob with Ruby so I was fun to read about gsub, how to modify the replace and stuff.

        By the way, I did a generic config.rb on my github (with credits to you also).

        raphaelddl.github.io/compass-generic-config/

        Let me know if you got something to add/edit/remove to config.rb I made, I’m not 100% sure it’s good :P

        Best Regards,
        Raphael

        Reply
        1. Peter J Langley Post author

          Great work with the config.rb boilerplate on Github – I especially like the conditional environment check. I can imagine this being a very useful aspect, and I’ll be certain to use this as a base for any new projects. Thanks for sharing and mentioning me in the credits :)

          I’m sure we’ll speak again soon!

          Reply
  2. Schneid

    Thanks. I’m wondering if it is possible to customize the .grey-icons in your example, without changing the folder name. I’m working on the project and have sub-folders named the same way. For example, imagine having in your grey-icons folder the subdirectories ‘mobile’ and ‘desktop’ with your PNGs in there. Then you have another folder called red-icons with the same subdirectories ‘mobile’ and ‘desktop’. I end up having to do @include all-mobile-sprites and it only refers to the first folder. I could change the folder names, but would prefer not to. Do you know of a solution?

    Reply
    1. Peter J Langley Post author

      Hi. I apologise for not getting back any sooner.

      You can always do this:
      @import 'compass/utilities/sprites';
      @import "grey-icons/*.png";
      @import "grey-icons/desktop/*.png";
      @import "grey-icons/mobile/*.png";
      @include all-grey-icons-sprites;

      However, it does create an extra sprite per sub-directory, but your root sprite will get the combination of them all.

      Reply
  3. RaphaelDDL

    Compass is keeping both with and without random hash images when using those scripts on config.rb

    To not keep the one with hash, add “FileUtils.rm_rf(filename)” (with no quotes) in the line after “FileUtils.cp filename, filename.gsub(%r{-s[a-z0-9]{10}\.png$}, ‘.png’)” and all done, no double images.

    Regards

    Reply
      1. RaphaelDDL

        Okay.
        Btw, thank you very much for this and the ‘How to install sass and compass’ guides!
        Was getting mad without Terminal (I’m on Win7).

        Reply
  4. Luke Hill

    Thanks a lot for this article Peter, this really helped with understanding how compass image spriting works. As someone who is new to Compass and SASS it takes a bit of time getting used to all the code ‘magic’ which just happens, its nice to finally understand why it happens.

    Thanks again,
    Luke.

    Reply
    1. Peter J Langley Post author

      Hi – I’m not even sure if you can sprite file types other than PNGs.

      I’m sure you’ve already tried changing the extension (e.g. @import "grey-icons/*.gif)";) to see if that works? But if that doesn’t work, maybe you could just save your gifs/jpegs as pngs anyway? Even if they don’t have transparency.

      Reply
  5. Jack Whiting

    Wow, Never knew that SASS and Compass could do this for you, this is going to save me a huge amount of time when it comes to some projects! Thanks for the great tip.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>