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, ...)

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

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.