My LESS mixin for all icon sizes from one sprite image file

This LESS mixin will resize a background-image, via the CSS3 background-size property, so this single PNG image sprite file can be resized and reused for all icons sizes that are lower or equal to the originals.

Why PNG images, why not SVG?

I love working with vectorial images. They offer so much advantages. But they are time consuming. Must of the designers at my job create their icons in Photoshop pixels. In some case, when I have free time, I lunch my FreeInk app and RE-design my icons as SVG image. But since it’s written front-end developer on my paychecks, not designer, it is hard to justify my time spent in a drawing software instead of a (code) text editor.

In real (sprint demo) life HTML website development, time is money. Using PNG (24) for background-image sprites is so much faster for HTML websites that there is no real choice.

CSS 3.0 background-size to the rescue!

The background-size property is a stable CSS 3.0 that is supported by all modern browsers, including IE9.

Compare real dimensions of close icon sizes within the sprite background-image; The original 50x50 VS the required 34x34 pixels in Photoshop viewport
The original 50×50 close button dimension VS the required 34×34 pixels in Photoshop viewport
Web inspector screenshot showing my HTML source code and CSS code generated with my LESS mixin in it's context
Web inspector screenshot showing my HTML source code and CSS code generated with my LESS mixin in it’s context.

My bgSpriteFix mixin

I broke the details in four parts for your conveninece:

  1. The variables
  2. The mixin itself
  3. The mixin in context
  4. The rendered CSS

The variables lexicon

LESS mixin’s variables CSS property Description
1. @bgColor background-color – –
2. @bgImg_url background-image – –
3. @spriteIco_pxSize_original – – The original size of the icons as they are designed within the original image-sprite.
4. @spriteIco_pxRequired_value width, height The required size for THIS icon. This variable should NOT higher the the original (@spriteIco_pxSize_original).
5. @imgFile_pxSize_w – – The outter width of the original image-sprite.
6. @imgFile_pxSize_h – – The outter height of the original image-sprite.
7. @bgImg_pxPosition_x background-position Icon’s horizontal position within the original image-sprite
8. @bgImg_pxPosition_y background-position Icon’s vertical position within the original image-sprite
9. @bgImg_pxPosition_y_hover background-position Icon’s vertical position on mouse hover state

My LESS mixin bgSpriteFit

/*
* BACKGROUND-IMAGE SPRITE FIT
*
* This mixin resize a background-image via the background-size property.
* Requirements:
* * icon ratio 1:1 (square shape),
* * the first & second var are strings, all the others are digits ONLY,
* * don't put the 'px', only value as digit!
*
* -----------------------------------------------------------------
* DATA EXAMPLE FOR REFERENCE
*
* 1) @bgColor:                     transparent; //default
* 2) @bgImg_url:                   '/img/sprite/btn-50x50-sprite.png';
*
* // icon sizes:
* 3) @spriteIco_pxSize_original:   50;
* 4) @spriteIco_pxRequired_value:  34;
*
* // image-sprite size:
* 5) @imgFile_pxSize_w:            300;
* 6) @imgFile_pxSize_h:            150;
*
* // icon position within the image-sprite:
* 7) @bgImg_pxPosition_x:          200;
* 8) @bgImg_pxPosition_y:          50;
* 9) @bgImg_pxPosition_y_hover:    100;
*
*/
.bgSpriteFit(@bgColor:transparent, @bgImg_url, @spriteIco_pxSize_original, @spriteIco_pxRequired_value, @imgFile_pxSize_w, @imgFile_pxSize_h, @bgImg_pxPosition_x:0, @bgImg_pxPosition_y:0, @bgImg_pxPosition_y_hover:0) when (isnumber(@spriteIco_pxSize_original)) and (isnumber(@spriteIco_pxRequired_value)) and (isstring(@bgImg_url)) and (isnumber(@imgFile_pxSize_w)) and (isnumber(@imgFile_pxSize_h)) and (isnumber(@bgImg_pxPosition_x)) and (isnumber(@bgImg_pxPosition_y)) and (isnumber(@bgImg_pxPosition_y_hover)) {

    // calculate ratio
    @spriteResize_ratio_value:          floor(((100/@spriteIco_pxSize_original)*(@spriteIco_pxRequired_value))); //calcul
    @spriteResize_ratio:                ((@spriteResize_ratio_value)/100); //pourcentage in decimals. ex: 75% = 0.75
    //turn it in a pourcentage string
    @spriteResize:                      ~'@{spriteResize_ratio_value}%'; //pourcentage string interpolation
    // calculate background-size height value
    @imgFile_pxResized_h_value:         floor(((@imgFile_pxSize_h)*(@spriteResize_ratio)));
    //turn it in a pixel string
    @imgFile_pxResized_h:               ~'@{imgFile_pxResized_h_value}px'; //pixel string interpolation
    //turn pxRequired in pixel string
    @spriteIco_pxResized :              ~'@{spriteIco_pxRequired_value}px'; //pixel string interpolation

    // re-calculate background-position after applying resized value
    @resized_bgImg_pxPosition_x_value:  floor(((@bgImg_pxPosition_x)*(@spriteResize_ratio))); //calcul (ex: 250px * 68% = 170)
    @resized_bgImg_pxPosition_y_value:  floor(((@bgImg_pxPosition_y)*(@spriteResize_ratio)));  //calcul (ex: 50px * 68% = 34)
    @resized_bgImg_pxPosition_y_hover_value: floor(((@bgImg_pxPosition_y_hover)*(@spriteResize_ratio)));  //calcul (ex: 100px * 68% = 68)
    //turn them in pixel strings
    @resized_bgImg_pxPosition_x:        ~'@{resized_bgImg_pxPosition_x_value}px'; //pixel string interpolation
    @resized_bgImg_pxPosition_y:        ~'@{resized_bgImg_pxPosition_y_value}px'; //pixel string interpolation
    @resized_bgImg_pxPosition_y_hover:  ~'@{resized_bgImg_pxPosition_y_hover_value}px'; //pixel string interpolation

    /*
     * render
     */
    width: @spriteIco_pxResized;
    height: @spriteIco_pxResized;
    background: @bgColor url(@bgImg_url) no-repeat ~'-@{resized_bgImg_pxPosition_x}' ~'-@{resized_bgImg_pxPosition_y}';
    background-size: auto (@imgFile_pxResized_h);
    // hover state
    &:hover {
        background-position: ~'-@{resized_bgImg_pxPosition_x}' ~'-@{resized_bgImg_pxPosition_y_hover}';
    }
}

The mixin script used within the context of the 34×34 modal close button

.modal-header {
    .close {
        position: absolute;
        opacity: 1;
        right: 40px;
        border: none;

        // my mixin
        .bgSpriteFit(transparent, '/img/sprite/btn-50x50-sprite.png', 50,34, 300,150, 250,50,100);
    }
}

The LESS rendered as CSS

.modal-header .close {
    position: absolute;
    opacity: 1;
    right: 40px;
    border: none;

    /* rendered mixin */
    width: 34px;
    height: 34px;
    background: transparent url('/img/sprite/btn-50x50-sprite.png') no-repeat -170px -34px;
    background-size: auto 102px;
}
.modal-header .close:hover {
    background-position: -170px -68px;
}