7
votes

I am using compass to generate sprite images. And I have MANY sprite icons, and it is generating too much CSS code (too many class selectors for the background image). So lets analyze the compass sprite code:

as you can see here http://compass-style.org/help/tutorials/spriting/

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

Will generate:

.my-icons-sprite,
.my-icons-delete,
.my-icons-edit,
.my-icons-new,
.my-icons-save   { background: url('/images/my-icons-s34fe0604ab.png') no-repeat; }

.my-icons-delete { background-position: 0 0; }
.my-icons-edit   { background-position: 0 -32px; }
.my-icons-new    { background-position: 0 -64px; }
.my-icons-save   { background-position: 0 -96px; }

If you see I use this way: <div class="my-icons-sprite my-icons-delete"></div>

I want Compass to generate this code:

.my-icons-sprite { background: url('/images/my-icons-s34fe0604ab.png') no-repeat; }

.my-icons-delete { background-position: 0 0; }
.my-icons-edit   { background-position: 0 -32px; }
.my-icons-new    { background-position: 0 -64px; }
.my-icons-save   { background-position: 0 -96px; }

Else each new image, it'll add for background and background position. Causing too many selectors.

Is there a configuration for that?

Thanks

5
The main idea behind Compass' sprites, is that you only have to use one class on your element. Do you think having a leaner stylesheet outweighs having leaner HTML? - Joseph Silber
You are perfectly right! Even if I use once it'll be smaller - Thiago Festa

5 Answers

8
votes

Have you tried this snippet for Compass?

$icons: sprite-map("icons/*.png");

i{
    background: $icons;
    display: inline-block; // or block
}

@each $i in sprite_names($icons){
    .icn-#{$i}{
        background-position: sprite-position($icons, $i);
        @include sprite-dimensions($icons, $i);
    }
}

This example uses the <i></i>-tag with a class containing the prefix icn- combined with the filename of the separate .png-files in your icons-folder. Like this:

<i class="icn-delete"></i>

The generated CSS looks like this:

i {
    background: url('/path/to/generated/spritemap/my-icons-xxxxxxxxxxx.png');
    display: inline-block;
}
.icn-delete {
     background-position: 0 0;
     height: 32px; // assuming the width is 32px
     width: 32px; // assuming the height is 32px
}
.icn-edit{
     background-position: 0 -32px;
     height: 32px; // assuming the width is 32px
     width: 32px; // assuming the height is 32px
}
.icn-new {
     background-position: 0 -64px;
     height: 32px; // assuming the width is 32px
     width: 32px; // assuming the height is 32px
}
...
..
.

Still, I haven't quite figured out how to use this in combination with Compass' Magic Selectors.

Magic Selectors works very nice when you need different states (:hover, :active, :target). All you have to do is name your files like this: filename_state.png (delete_hover.png, delete_active.png etc). Compass' Magic Selectors then automatically generates css for :hover, :active and :target (delete:hover, delete_hover and delete-hover). This way you are quite free to choose how you would represent a state-change.

If you, in my first example, has filenames with the postfix for hover/ active states, the snippet only writes CSS like this:

.icn-edit_hover {
    background-position: -32px -32px;
    height: 32px;
    width: 32px;
}

I'd really like to have it print this:

.icn-edit:hover, .icn-edit_hover, .icn-edit-hover{
    background-position: 0 -32px;
    height: 32px;
    width: 32px;
}

like the traditional Compass' Magic Selectors does. Any idea?

4
votes

In my opinion, it seems like the best of both worlds (less HTML and CSS) would be to have this code (using an attribute selector for the image):

HTML

<div class="my-icons-delete"></div>

CSS

[class^="my-icons-"] { background: url('/images/my-icons-s34fe0604ab.png') no-repeat; }

.my-icons-delete { background-position: 0 0; }
.my-icons-edit   { background-position: 0 -32px; }
.my-icons-new    { background-position: 0 -64px; }
.my-icons-save   { background-position: 0 -96px; }

Unfortunately, I do not know how to get Compass to export like that. However, unless you are using Compass dynamically rather than just to build your back end static css, you could just change it once generated.

1
votes

For anyone looking to the answer to ScottS question. How can I use a css selector for anything starting with a baseclass

Try this: http://codepen.io/Acts7/pen/nwsEb

I'm pasting the code below.

the spriteGen mixin requires two parameters 1) the baseclass you want to use (in ScottS case --- "myicons" 2) the second parameter is the folder location

Also DONT forget the "." before #{$mySpriteBaseClass}. Otherwise you get >> myicons-home_icon{background-position:...} (notice no . for class name selector)

// _custom.scss
// ---------------------------------------------------------
//  Sprite Generation
    --------------------- */
    @include spriteGen('sprites','sprites');

// _mixins.scss
// ---------------------------------------------------------
// Sprite Generation Mixin with options
@mixin spriteGen($mySpriteBaseClass:'.spritebc',$mySpriteFolder:'sprites'){
    $mySprites:$mySpriteFolder + "/*.png";
    $spritefoldername-map: sprite-map($mySprites,
        $spacing: 10px, 
        $layout: vertical
    );
    //  if using base class as starter for sprite name class
    [class^="#{$mySpriteBaseClass}"]{
    /*//    if using a separate base class
    .#{$mySpriteBaseClass}{*/
        //      TODO: 
        //      Add if/else to set width globally 
        //      or let spriting assign it per each
        //width: 48px;
        //height: 48px;
        display: inline-block;
        vertical-align: middle;
        background: $spritefoldername-map no-repeat;
    }

    @each $sprite in sprite_names($spritefoldername-map) {
        //  if using sprite base class as prefix to full sprite class name
        .#{$mySpriteBaseClass}-#{$sprite} {
        /*//    if using separate base class and sprite name class
        .#{$sprite} {*/
            background-position: sprite-position($spritefoldername-map, $sprite);
            @include sprite-dimensions($spritefoldername-map, $sprite);
        }
    }
}
0
votes

What's wrong with the current output? You can already assign my-icons-delete/edit/new/save only, this is semantic enough - it already says it's an icon and it's a delete icon.

0
votes

This is what I'm currently doing, it requires Sass 3.3 though:

$icons: sprite-map('icons/*.png');

.icon {
    background: $icons;
    display: inline-block;
    vertical-align: middle;
}

@each $i in sprite_names($icons) {

    $underscore: str-index($i, _);

    @if ($underscore < 1) {
        .icon--#{$i} {
            background-position: sprite-position($icons, $i);
            @include sprite-dimensions($icons, $i);
        }
    } @else {
        $prefix: str-slice($i, 0, $underscore - 1);
        $postfix: str-slice($i, $underscore + 1);

        .icon--#{$prefix}:#{$postfix} {
            background-position: sprite-position($icons, $i);
        }
    }

}

I'm using BEM here so it assumes you'll use this like <i class="icon icon--star></i>, so if if you have a "star.png" and "star_hover.png" images it'll generate .icon--star and .icon--star:hover class names.