Sassy Sprites - Making sprites easy with SCSS/Sass

in #programming7 years ago (edited)

I hate sprites

When talking about sprites, I often hear from people that they hate them. Unfortunately their biggest reason tends to be one which could be easily solved, the grueling task of setting the background position pixels for each icon.

Before I go further, I do want to address that glyph based fonts are a great alternate to sprites, so use them when you can. For those times where fonts are not an option, I hope the information in this post can make things easier for you.

Building the Sprite

There are many tools out there that are great at helping build your sprites, including glup, grunt, bower and webpack. For this guide, I will be referencing when you are building your
When building your sprite there are some guidelines to follow that will make this work better for you.

  • Start by creating your sprite image as a square where each side is 11x the size of your typical icon. *(i.e. 48px x 48px icon, use 528px x 528px)
  • You don't have to use 11x this is just an example
  • 11 is utilized because it allows for even percents, in order to maintain the most accurate positioning of the icons.
  • For planning, use a number that divides into whole numbers or decimals with few points, since most browsers will only utilize up to 2 decimal points
  • You will need to increase the count of your columns/rows by 1 in order to accommodate the percents, since we will start our count at 0 and end at 100.
  • Recommended col/row counts: 2, 3, 5, 6, 11
  • 2: 0%, 100%
  • 3: 0%, 50%, 100%
  • 5: 0%, 25%, 50%, 75%, 100%
  • 6: 0% 20% 40% 60% 80% 100%
  • 11: 0%, 10%, 20%, [...], 90%, 100%
  • Next add guidelines to your image, which will help you to keep your icons aligned in their cell/cells
  • Add a guideline at each interval for both columns and rows (i.e. 48px, 96px 144px, ...)

Sprite grid with guidelines

  • Place your images in the grid. I recommend aligning based off of the top left of each cell, but you can align as you see fit as long as you are consistent.

  • For images that are larger than your grid, align them to the top left of the cell block, since you will be referencing them based on x/y coordinates and top left is going to be your reference point in css.

  • Crop your image if necessary, but try to crop to the nearest number that provides good percentages

Sprite example after crop

What is the SCSS function?

Now that you have your sprite, it is time to use the SCSS sprite function. The function is actually a quite simple one, you provide the x and y coordinates (as well as the number of cols/rows) and the offset percent is returned for the background-position. You can manually provide the number of rows and columns each time, but by using a default value, it is unnecessary.

Sample of variables I assign

$base-height:               30px
$sprite-image-path:         "";
$sprite-image-file:         "48px.png" !default;
$spriteCols:    11 !default;
$spriteRows:    5 !default;
$sprite-image-icon-height:  48px;
$sprite-image-icon-width:   48px;
$sprite-image-width:        528px !default;
$sprite-image-height:       240px !default;
$sprite-icon-height:        $base-height;
$sprite-icon-width:         $base-height;
$sprite-image-scaled-height:       ($sprite-image-height/$sprite-image-icon-height) * $sprite-icon-height;
$sprite-image-scaled-width:        ($sprite-image-width/$sprite-image-icon-width) * $sprite-icon-width;
$sprite-image-width: $sprite-image-scaled-width;
$sprite-image-height: $sprite-image-scaled-height;

sprite-location SCSS function

@function sprite-location($x_pos, $y_pos, $spriteCols: $spriteCols,     $spriteRows: $spriteRows){
  $x-shift: 0%;
  $y-shift: 0%;

  @if($spriteCols>1){
    $x-shift: 100% * ($x_pos / ($spriteCols - 1));
  }

  @if($spriteRows>1){
    $y-shift: 100% * ($y_pos / ($spriteRows - 1));
  }

  @return $x-shift $y-shift;
}

SCSS mixins

On top of the function, I utilize some mixins to make it the code look even cleaner. These mixins use the sprite-location function.

@mixin sprite-image($x: $x, $y: $y, $spriteCols: $spriteCols, $spriteRows: $spriteRows, $sprite-image-path: $sprite-image-path, $sprite-image-file: $sprite-image-file, $sprite-image-width:$sprite-image-width, $sprite-image-height: $sprite-image-height){
  background-image: url("#{$sprite-image-path}#{$sprite-image-file}");
  background-position: sprite-location($x, $y, $spriteCols, $spriteRows);
  background-size: $sprite-image-width $sprite-image-height;
}

@mixin sprite-position($x: $x, $y: $y, $spriteCols: $spriteCols, $spriteRows: $spriteRows){
  background-position:sprite-location($x,$y, $spriteCols, $spriteRows);
}

In Practice: Sample SCSS

In the following example, we will give the .sprite-icon class all the information it needs to prep the sprite for use, but not display any sprite on that class alone. The .sprite-icon-one and .sprite-icon-two classes will shift the position of the sprite to render the icons.

    .sprite-icon{
        @include sprite-image(-1, -1);
        width:$sprite-icon-width;
        height:$sprite-icon-height;
        &.sprite-icon-one{
            @sprite-position(0,0);
        }
        &.sprite-icon-two{
            @sprite-position(1,0);
        }
    }

Scaling sprites

You may not have noticed above, but the sprite is actually already being scaled. Instead of a 48x48 pixel icon, it is actually scaled to show 30x30 pixels. Using the following logic, you can scale from your base point.


@mixin sprite-scaled($scaledWidth, $scaledHeight, $sprite-image-width: $sprite-image-width, $sprite-image-height: $sprite-image-height, $sprite-image-icon-width: $sprite-image-icon-width, $sprite-image-icon-height: $sprite-image-icon-height){
  background-size: (($sprite-image-width/$sprite-image-icon-width) * $scaledWidth) (($sprite-image-height/$sprite-image-icon-height) * $scaledHeight);
}

.sprite-icon{
    @include sprite-image(-1, -1);
    width:$sprite-icon-width;
    height:$sprite-icon-height;
    &.sprite-icon-xl{
      @include scaled-sprite(48px, 48px);
      width:48px;
      height:48px;
    }
    &.sprite-icon-lg{
      @include scaled-sprite(32px, 32px);
      width:32px;
      height:32px;
    }
    &.sprite-icon-sm{
      @include scaled-sprite(24px, 24px);
      width:24px;
      height:24px;
    }
    &.sprite-icon-xs{
      @include scaled-sprite(16px, 16px);
      width:16px;
      height:16px;
    }
    &.sprite-icon-one{
        @sprite-position(0,0);
    }
    &.sprite-icon-two{
        @sprite-position(1,0);
    }
}

Using the above sample code you would now be able to get 5 different sizes of sprites all from one sprite image and without having to manually enter pixels to offset each sprite icon.

Good Luck

Good luck with your sprites and I hope they are now far easier for you to implement. If you have any questions, feel free to ask below and I will be happy to assist.

Sort:  

Sorry if the post has parts that seem disconnected. I wrote it once and uploaded and it disappeared, so this is rewritten, but I did it in segments.

Congratulations @irrelevant! You received a personal award!

Happy Birthday! - You are on the Steem blockchain for 2 years!

You can view your badges on your Steem Board and compare to others on the Steem Ranking

Do not miss the last post from @steemitboard:

The Steem community has lost an epic member! Farewell @woflhart!
SteemitBoard - Witness Update
Do not miss the coming Rocky Mountain Steem Meetup and get a new community badge!
Vote for @Steemitboard as a witness to get one more award and increased upvotes!