The Ecstasy and Agony of Compass Sprite Generation, Part 1

css

CSS preprocessors have become increasingly popular in recent years because of their capacity to do a lot of the most tedious parts of CSS for you automatically. (For an overview of the different CSS preprocessors available, see How to Choose the Right CSS Preprocessor.)

One of those tedious tasks is sprite generation. Cutting images, merging them into a single file, measuring their positions and recording that in your CSS can add hours to a project, and may need to be redone several times to get right or as artwork or project requirements change.  However, the Compass CSS framework, which uses the Sass CSS preprocessor, can streamline the spriting process for you by generating your sprites automatically.  All you have to do is throw your images into a directory, give Compass some basic instructions, and it will do all the heavy lifting for you.  But is it really that easy?

In this post we will examine the promise of automatic generation with Compass, and learn how to generate our own sprite code.  In my next post, we’ll take a look at some of the pitfalls with using Compass sprites and see where it falls just a little bit short of perfect.

The Ecstasy

Creating CSS sprites in Compass borders on performing magic.  Let’s get started.

Setting the Stage

For our tutorial, we’re going to create a very basic page with a horizontal navigation bar using an unordered list for its markup.  Associated with each menu option will be an icon that one would typically find laid out in a sprite file.

Let’s start with the markup for our unordered list:

<ul>
    <li><a href="/news/" class="news">Go to News</a></li>
    <li><a href="/comments/" class="comment">Leave a Comment</a></li>
    <li><a href="/contact/" class="touch">Get in Touch</a></li>
    <li><a href="/store/" class="buy">Buy Something</a></li>
 </ul>

Before we can even get to adding our icons, our menu needs a little layout and typography love.  We’ll apply some basic styling to make it the horizontal nav of our dreams. (Note: all Sass examples in this tutorial are in the SCSS syntax.)

ul {
    background-color: #dfdfdf;
    overflow: hidden;

    li {
        float: left;

        a {
            color: #222;
            display: block;
            font-weight: bold;
            margin-right: 15px;
            padding: 0.5em 0.5em 0.5em 35px;
            position: relative;
            text: {
                decoration: none;
                transform: uppercase;
            }
        }
    }
}

So far we’ve come up with something like the image below.  Note that I’ve left room for adding our sprites to the left of our menu options.

A bare horizontal navigation, before the application of icon sprites.

Our navigation menu, before we add on sprites.

Applying Our Sprites

I’ve created a small icon to go with each of these options. My next step is to cut them individually and throw them into a single directory together which I’m going to call “nav-icons”.  Our directory structure now looks something like:

/(Document Root)
    images/
        nav-icons/
            comment.png
            newspaper.png
            shoppingcart.png
            telephone.png
    index.html

Next, we need to tell Compass about my new icons.  So we’ll add the following lines to the top of our Sass file.

@import "nav-icons/*.png";
@include all-nav-icons-sprites;

Be aware that the @import statement relies on the images_dir variable in your config.rb file to find the appropriate directory, which is “images” by default. Ergo, the “nav-icons” directory in our example is a subdirectory of “images,” which in turn is at the document root.

Now you have an automatically generated sprites file as well as a group of associated classes that point to the correct file as a background image and the correct background position for each sprite.

The output of our Compass sprites code.

Compass did this.

Compass generated the following CSS:

.nav-icons-sprite, .nav-icons-comment, .nav-icons-newspaper, .nav-icons-shoppingcart, .nav-icons-telephone {
    background: url('/images/nav-icons-sa6253eb170.png') no-repeat;
}

.nav-icons-comment {
    background-position: 0 -73px;
}

.nav-icons-newspaper {
    background-position: 0 -51px;
}

.nav-icons-shoppingcart {
    background-position: 0 0;
}

.nav-icons-telephone {
    background-position: 0 -26px;
}

Now here’s where my tutorial is going to vary a bit from what the Compass documentation is going to tell you. Compass states that you can apply the generated CSS classes to your markup as needed. To me, this seems somewhat awkward and unnecessary. What if we’re in a CMS environment where we don’t have that fine control of the markup? Or our client can’t be relied upon not to alter the markup in a way where the CSS classes break? There’s a number of scenarios where directly utilizing Compass’ class names is not practical. Yes, you can actually have compass use different class names if you’d like, but I have a more elegant solution, courtesy of Sass itself.

Enter @extend

@extend is, in brief, a means of including the definitions of one class in other classes. So instead of altering our precious markup, we will take our markup’s existing classes and apply the generated class selectors to them.

As you saw in the original markup, I already have class names associated with each of the menu items. In my Sass, I’ll extend each of these classes with the “magic” classes that Compass created. Adding these selectors to our Sass document, my revised code looks like:

ul {
    background-color: #dfdfdf;
    overflow: hidden;
    padding: 0 5px;

    li {
        float: left;

        a {
            color: #222;
            display: block;
            font-weight: bold;
            margin-right: 15px;
            padding: 0.5em 0.5em 0.5em 35px;
            position: relative;
            text: {
                decoration: none;
                transform: uppercase;
            }

            &.news {
                @extend .nav-icons-newspaper;
            }
            &.comment {
                @extend .nav-icons-comment;
            }
            &.touch {
                @extend .nav-icons-telephone;
            }
            &.buy {
                @extend .nav-icons-shoppingcart;
            }
        }
    }
}

In this simplistic example, it’s hard to see the benefit of using @extend versus changing our markup to match what Compass is generating for us. Using @extend offers us flexibility that we otherwise wouldn’t have, such as using CSS3 pseudo-selectors such as :nth-of-type instead of classes in the first place. @extend is a very powerful Sass feature, and leveraging it here is a great way to maximize our efficiency.

The Results, Take 1

So let’s take a look at our new navigation menu.

A horizontal navigation bar with icon sprites that bleed together.

Our first attempt at adding Compass sprites to our navigation.

Oh, my. That isn’t going to do, is it? With no spacing between the sprites, but padding in the nav bar, our icons are bleeding into each other’s spaces. Fortunately, we can tell Compass to give each of the icons a little breathing room. 20 pixels should be more than enough. To do so, let’s add the following line before our @import "nav-icons/*.png" statement:

$nav-icons-spacing: 20px;
The result of our Compass-generated sprites, with 20px of spacing applied between icons.

With the spacing defined, our icons now have some personal space.

The Results, Take 2

With the increased spacing, our nav now looks like:

Our horizontal navigation with spacing applied to the icon sprites.

A little breathing room for our sprites.

Well, that’s a little better, but we don’t like that all of the sprites are flush with the top of our nav bar. Essentially Compass, like any good computer program, is giving us exactly what we’re asking for, if not what we want: the precise background positions of each of our icons in the sprite file. This is great except that the designer is going to flip when he sees this. We need to adjust things a little bit to get things aligned the way we want them.

When we imported our images in the beginning of this exercise, we also added a bunch of mixins to our toolkit to help us with these kind of issues. Let’s see if one of those can help us now.

sprite-background-position

The sprite-background-position mixin allows us to add some offset parameters in order to adjust the positioning of the sprite on our page. This involves a little trial-and-error in our browser debugger, but a little adjustment in Firebug lets me know that the “Go to News,” “Leave a Comment” and “Buy Something” icons need to be adjusted down by 4 pixels, while the “Get in Touch” icon should be adjusted down by 3 pixels. So let’s add the necessary code into our Sass:

&.news {
    @extend .nav-icons-newspaper;
    @include sprite-background-position($nav-icons-sprites, newspaper, 0, 4px);
}
&.comment {
    @extend .nav-icons-comment;
    @include sprite-background-position($nav-icons-sprites, comment, 0, 4px);
}
&.touch {
    @extend .nav-icons-telephone;
    @include sprite-background-position($nav-icons-sprites, telephone, 0, 3px);
}
&.buy {
    @extend .nav-icons-shoppingcart;
    @include sprite-background-position($nav-icons-sprites, shoppingcart, 0, 4px);
}

Notice the formatting of the mixin variables: the sprite map variable, followed by the specific sprite icon we’re positioning, followed by the X-axis offset, and finally the Y-axis offset.

Unfortunately, by adding another line controlling the positioning of the icon, we’re losing some of the elegance that using the @extend directive gave us, as well as adding somewhat to code bloat since we’re having to define the background positioning twice. However, you won’t need to offset the positioning of every sprite in every project, so using @extend may still be the best approach in many circumstances.

The Results, Take 3

Horizontal navigation with icon sprites that having spacing and offsets applied.

Our final, final result.

Finally, we have results that look nicely aligned. But in the process, we got a little taste of some of the difficulties we face when we try to let computers make the decisions for us.

Stay Tuned for Part 2

In the next part, we’re going to look at more problems that can arise in Compass sprite generation when we want to venture into HiDPI sprites and other more complex use cases.

Free Workshops

Watch one of our expert, full-length teaching videos. Choose from either HTML, CSS or Wordpress.

Start learning

Tracy Rotton

Tracy Rotton is a web developer for RP3 Agency in Bethesda, Maryland with a passion for standards-based front-end development. A big fan of WordPress, she is a contributor to the new WordPress default theme, Twenty Thirteen. When Tracy isn't building websites, she enjoys skiing, watching Redskins football and spending time with her two young children.

Comments

4 comments on “The Ecstasy and Agony of Compass Sprite Generation, Part 1