2 Comments

Working with Compass Sprites — Lessons Learned

Spriting images for web development has a few benefits that improve page load time. This is my second attempt at using Compass Sprites. The first time I tried to implement it, I ran into a problem that caused me to put it down for a while. But the benefits seemed too good to not pick it up again. The good news is that I found a way to make it work, thanks to some tips posted by readers, and was quickly able to implement it.

In the end, I created 3 separate sprites for different purposes. The first sprite is the largest and holds most of the images and uses the default vertical layout. The second sprite uses a horizontal layout, and the 3rd is for repeating images only. They were all comprised of png files and produced png sprites.

Through this process, I found some insights, some pleasant surprises, and also some work arounds that were worth sharing.

How To Set Up Compass Sprites

Here’s a quick overview of how to get Compass Sprites up and running.

  1. Put all the images you want to make into a sprite in one folder. I called mine “sprites,” but you could name it “icons,” or “combined,” or whatever makes sense for your project.
  2. Import the sprite into your stylesheet via @import “yourfoldername/*.png”. Compass will automatically generate the sprite sheet for you.
  3. Insert the Compass Sprite mixin where you would normally put a background image in your Sass, like +yourfoldername-sprite(nameofimage).

That’s it! Compass will take care of the rest. It knows where the image you called is in the CSS and positions it automatically. The generated CSS will look something like:

background: url("/assets/sprites-sa34a6d7aa5-e851c4ce7c78d99f04f7855226a50cfc.png") no-repeat scroll 0 0 transparent
background-position: 0 -202px

There are 2 separate background definitions because the first defines the same background for all the selectors in your Sass that use the sprite. The second defines the background position for each specific image. Don’t worry, it just works.

Benefit: Track Your images

Going through the process of using Compass Sprites is a good exercise to actually look at your images. This is just good ol’ fashioned maintenance. Over time, as the app is being developed, images become unused and forgotten for one reason or another. I found a few images that weren’t being used at all in the app. But the interesting thing is that I had already gone through and purged several images before starting the spriting process. The fact that I found a couple more images to remove was a bit surprising, but good.

Benefit: Reduced File Sizes — By a Long Shot!

One of the most pleasant surprises I encountered was the sheer reduction in file size that Compass Sprites produced. The large sprite sheet contained 50 separate images that totalled almost 95 kb before the conversion and only 55 kb after being sprited. The sprite containing repeating images went from 8 images weighing about 8kb to a total 450 bytes in the sprited image.

The big reduction in file size occurs when the overhead goes down because of the way png files are compressed. Not to mention, it’s also a great savings in number of hits to the server going from 8 images to one.

Tip: Add Margin Between Images in the Sprite as Necessary

At the start of your process, set the sprite spacing to as little as possible and increase it as necessary along the way — if needed. By default, Compass Sprites sets 0 pixels of space between each image. I found this problematic because some of my images were placed in containers that had extra padding around them for positioning in the layout.

I started my spacing at 10px and worked my way up to 14px as I found different places in the layout that needed more spacing.

$sprites-spacing: 14px

Beware that adding empty space in a sprite adds weight to the overall sprite file size. The more space you set between the images, the heavier the file will be. In big sprites containing a lot of images, weight can add up quickly. Test the file size of your sprite if you make a change to the spacing.

Tip: Add extra margins to specific images when necessary

One of the concerns I had using an auto-generated sprite was that all the images would be placed in the sprite homogeneously—the spacing between them being same. This could be problematic if there is any padding around the image for layout purposes. Most of the images in our app have at least a small amount of space around them in the layout. If the sprite spacing was too small, pieces of other images would start to be seen on the page. Compass Sprites offers a way to change the spacing of one-off images if needed.

$sprites-nameofimage-spacing: 30px

If I had designed the containers to be the same size as the images, this would not be a problem and I could have used a sprite spacing of 0. Google used to use a sprite like this.

I found a balance between adding spacing everwhere vs. adding spacing only where I needed it.

Tip: Work down the cascade

For styles that are over-riding a previous background definition, make sure to add Compass Sprites for each selector all the way down the chain. The mixin may be written in a different way than what you’ve previously specified your background images to be, which may cause the CSS to break.

Technique: Repeating images in sprites

Several of the images in this app used images that repeat along the x axis. I chose to make one sprite containing only repeating images. In Compass Sprites, repeating images have to fill the entire width of the sprite. I chose to make a separate sprite for all the repeating images in our app by making them all the same width. (My repeating images require a specific width because they contain a pattern.)

If width is not a factor for your images, there are other ways to implement repeating images in Compass Sprites that doesn’t require a separate sprite sheet.

Technique: Positioning and aligning images

To position an image within the container, I took advantage of offset-x and offset-y. This adjusts Compass’s interpretation to move the image up, down, right or left within the container.

In my Sass for a particular selector, I include these parameters in the mixin:

h2
	+sprites-sprite(nameofimage, $offset-x: 3px, $offset-y: 9px)

The x and y offsets were the same as the x and y positions in my previous background definition:

h2
	background: url(nameofimage.png) no-repeat 3px 9px

Right-aligned images

Some images in the layout requried a background image to be aligned to the right, center or bottom of the container. Normally, I would use a background position like this:

h2
	background: url(nameofimage.png) no-repeat right 0

In Compass Sprites, I had to first right align the image in the sprite to acheive this, which can be done in your stylesheet:

$sprites-nameofimage-position: 100%

Because this sprite has a vertical layout, setting the image position to 100% puts it at the very right-hand side of the image.

Then, the x-offset can be set to 100% in the Sass to inform Compass how to generate the CSS.

h2
	+sprites-sprite(nameofimage, $offset-x: 100%, $offset-y: 0)

Centered images

Similarly, center images by setting the position of the image in the center of the sprite and offsetting the x position to 50%.

$sprites-nameofimage-position: 50%

h2
	+sprites-sprite(nameofimage, $offset-x: 50%, $offset-y: 0px)

I first assumed that Compass would put the left-most part of the image at the center of the sprite, but Compass actually centers the image in the centerpoint of the image. The x-offset also centers the image, which works perfectly to center background images.

Bottom-aligned images

Images that need to be aligned to the bottom of the container are dealt with a little differently. I created a separate sprite for all the images I needed to align to the bottom of the container called “horizontal”, which used the horizontal layout option in Compass Sprites.

$sprites-horiz-layout: horizontal

The name of the folder for my horizontal images is called “horiz”.

Then, I moved the images to the bottom of the sprite using a similar technique as above by setting the position of the images to 100%.

$sprites-nameofimage-position: 100%

This time, because the layout of the sprite is horizontal, Compass assumes that 100% should put the images to the bottom of the sprite.

To position the image correctly in my Sass, I give the y-position a defition if 100%.

h2
	+sprites-horiz-sprite(nameofimage, $offset-y: 100%)

As far as I can tell, the shorcuts of “right”, “center” and “bottom” are not translated by Compass, so I stuck with percentages.

Found and fixed: Bug in Compass Sprites

We encountered a bug where the offset-y position wasn’t taking an input of 100%. When I entered 100%, Compass replaced it with 100px.

h2
	+sprites-horiz-sprite(nameofimage, $offset-y: 100%)

A fellow team member, Jared, forked the project on github and fixed the bug. To take advantage of this, put this in your gemfile:

gem 'compass', git: 'git://github.com/JaredSartin/compass.git', branch: 'stable'

Our environment setup: Rails 3.2.8, Sass 3.2.1, Compass 0.12.2 (based on the github branch above).