Last Updated: September 27, 2021
·
3.56K
· lucascsilva

Compass Sprites as Placeholders

[EDIT] A better solution would be to add this option to Compass core. I've created a pull request for that at https://github.com/chriseppstein/compass/pull/1448


What to know before reading

To fully understand this tutorial, you should be familiar with the concepts of compass sprite generation and magic imports. To read more on that, please refeer to the Spriting with Compass guide and the sections listed on that page.

Furthermore, it will be good to have some understanding of Sass placeholders. For that, there is a great article too on Extending Placeholder Selectors with Sass.

The problem

By default, using compass' magic imports for sprites will generate class selectors, that can then be extend in order to apply the background image to another selector.

The problem is usually you have one file importing all sprites, and this file, every time it is imported, will create some outputting CSS that you may or may not be using.

To solve this problem there should be a way to import sprite selectors as placeholders, so that no style selector will be output unless you actually extend them.

There is an issue on Compass detailing this problem.

Solution

To make the solution below easier to understand, lets use as an example the creation and import of an "icons" sprite.

1) Define base selectors

Compass lets you change the base selector for each importing sprite. Changing this will cause the main sprite selector for one sprite to be whatever you want: even a placeholder. The only thing we would have to do, in our example, is:

$icons-sprite-base-class: "%icons-sprite"

2) Import and compile images:

Now, we can import our sprite images.

@import "icons/*.png"

This is when Compass creates a "magic" Sass partial, containing not only the selectors, but the mixins to make use of the sprite functionality. Usually, by doing only this import there would already be a outputting selector, that in our case would be ".icons-sprite". As we changed it before, there will be no output.

3) Include custom sprite mixin

As I explained in the step above, the import of the sprite images will cause some mixins to be generated. Among these mixins, Compass imports the "compass/utilities/sprites/base", which defines a "sprites" mixin. This one mixin is our problem for it is responsible for creating our sprite selectors, but unlike what we did on the step 1, we can't interact with it for changing how it creates the selectors. It simple uses a class selector by default. But what we can do is overwrite this mixin to match our needs. So at this point we are going to add our custom mixin as below:

=sprites($map, $sprite-names, $base-class: false, $dimensions: false, $prefix: sprite-map-name($map), $offset-x: 0, $offset-y: 0)
  @each $sprite-name in $sprite-names
    @if sprite_does_not_have_parent($map, $sprite-name)
      $full-sprite-name: "#{$prefix}-#{$sprite-name}"
      %#{$full-sprite-name}
        @if $base-class
          @extend #{$base-class}
        +sprite($map, $sprite-name, $dimensions, $offset-x, $offset-y)

Visit http://compass-style.org/reference/compass/utilities/sprites/base/#mixin-sprites to see the default version of this mixin.

4) Include sprites

Finally, we would do as usual and include our sprites.

+all-icons-sprites(true)

Keep in mind this is only one of many ways for using compass sprites, and that steps 3 and 4 are only necessary for this specific way. In my opinion this is the best way to use sprites as, for now on, I would only have to use extends to make use of it, and not mixins; what will keep my outputting CSS smaller. Follow the step 5 for further instructions.

5) Usage with @extend

So, now you have all the sprites "compiled" as placeholder selector; you won't see them outputted to the CSS, obviously, because placeholder won't output unless they are extended. The naming rule of these placeholder selectors follows the same that would be used with class selector (%[DIRECTORY]-[IMAGE]), only with a preceding % in place of the usual .. Say we have an image named "close" in the icons sprite, it would be extended as so:

.foo
  @extend %icons-close

Ready to use

Here is the final Sass code ready to use to make the sprites be outputed as placeholders:

// Step 1.
$icons-sprite-base-class: "%icons-sprite"

// Step 2.
@ import "icons/*.png"

// Step 3.
=sprites($map, $sprite-names, $base-class: false, $dimensions: false, $prefix: sprite-map-name($map), $offset-x: 0, $offset-y: 0)
  @each $sprite-name in $sprite-names
    @if sprite_does_not_have_parent($map, $sprite-name)
      $full-sprite-name: "#{$prefix}-#{$sprite-name}"
      %#{$full-sprite-name}
        @if $base-class
          @extend #{$base-class}
        +sprite($map, $sprite-name, $dimensions, $offset-x, $offset-y)

// Step 4.
+all-icons-sprites(true)

// Step 5. (usage)
div
  @extend %icons-IMAGE_NAME

Please note the additional space between the @ and the import "icons/*.png" after the step 2; Coderwall insists in transforming it into a mention if I remove the space.

For each new sprite, you will have to repeat steps 1, 2, 4 and 5 of this tutorial. The third step, though, can remain the same.

Conclusion

This solution is not a definitive answer to this issue, as this relies on the overwriting of Compass mixins; what could cause problems on new to come versions of the framework. However, it sure gives us a way to enjoy Compass sprites today without messing too much our outputted CSS files.

I would gladly hear your opinion on this solution!

5 Responses
Add your response

nice done mate! thanks!

over 1 year ago ·

You can do this with core functionality, see https://gist.github.com/morewry/6613650

over 1 year ago ·

Hey, Rachel!

Yeah, I've seen you gist before. In fact, that was where I started to understand the problem and imagine a solution. But what I'm trying to solve here is mostly the behavior of the "all-[NAME]-sprites" mixin, as you yourself pointed out as the remaining problem at https://github.com/chriseppstein/compass/issues/1005.

Anyway, your gist solution is awesome to solve this problem, but if you have many sprite maps on the site you would eventually end up with lots of code for each of them to work.

Both solutions, however, should not be necessary if there was some kind of option to do so from the core. I made a pull request to Compass that, if accepted, would do just that. You can find it at https://github.com/chriseppstein/compass/pull/1448.

Thanks for the answer!

over 1 year ago ·

Could you please include an implementation example of extending a Sprite? My compass is saying it can't find my placeholder, and you can't look in a compiled css file for placeholders, so I have no way of seeing how I named it wrong. Could you give an example of a corresponding final placeholder name based on your code example above?

over 1 year ago ·

Hey there, dc2!

So, I just added a "Step 5" to this tutorial to help you out with this. If you still find some problem to use it, please let me know! Also, feel free to contact me on my (newly recovered) Twitter: @cnstntino.

over 1 year ago ·