Standard and retina display sprites with compass – how to

19/07/12

First of all, if you are not using sass/compass yet – you definitely should start right now. There are plenty of resources on how to install compass on mac or PC. Myself, I’m using a mac app “Codekit”.
One of the of compass features that are especially compelling is “spriting”. With compass you simply place all your images in a folder and import them into your stylesheet.
In this post I’ll share with how I prepare my sprites in two “versions” for standard and retina display.
Click on the image below to see the demo (check it on your retina display).

Spriting with compass for standard and retina display

Let’s follow two simple examples :

Example 1 – button

Let’s prepare a button with three states normal, hover and target. I prepare the three states of buttons in two sizes (normal and doubled for retina display). I save the separate .pngs in two folders : button and button2x.
The button folder will contain : button.png, button-hover.png and button-target.png. Identically button2x will contain : button.png, button-hover.png and button-target.png.

You can create and work with sprites compass in two ways. The first one would be the import-based approach :
@import "button/*.png"; @include all-button-sprites;
You can also specify a configurable variable before importing the sprite.

  


/* we'll use some mixins included in Compass Sprites Base */
@import "compass/utilities/sprites/base";
/* Set the variable
we'll have to assign background : $tut-sprites and background : $tut-sprites2x somewhere so that the sprite files were created */
$tut-sprites: sprite-map("button/*.png");
$tut-sprites2x: sprite-map("button2x/*.png");
/* I define a new mixing sprite-selectors2x, a retina version of sprite-selectors included in Compass Sprites Base */
/* We need to make sure that each sprites have a good background position - that's because the order of sprites in normal and retina versions is not necessarily the same */  
@mixin ph-sprite-selectors2x($map, $map2x, $sprite-name, $full-sprite-name, $offset-x: 0, $offset-y: 0) {	
  @each $selector in $sprite-selectors {
    @if sprite_has_selector($map2x, $sprite-name, $selector) {
      &:#{$selector}, &.#{$full-sprite-name}_#{$selector}, &.#{$full-sprite-name}-#{$selector} {
	@if (nth(sprite-position($map2x, #{$sprite-name}_#{$selector}),2) != 2*nth(sprite-position($map, #{$sprite-name}_#{$selector}),2) ) {
           background-position: $offset-x round(nth(sprite-position($map2x, #{$sprite-name}_#{$selector}, 0, 2*$offset-y), 2)/ 2);
	}
      }
    }
  }
}
/* Below the mixin that sets the proper background-positions, adds dimension (if desired) for both normal and retina display, */
/* Again we have to check whether the sprites order hasn't changed between normal and retina and update the background position if necessary */
@mixin ph-sprites($map, $map2x, $_item, $dimensions:false, $offset-x: 0, $offset-y: 0) {
	
	background-position: sprite-position($map, $_item, $offset-x, $offset-y );
	@if $dimensions {@include sprite-dimensions($map, $_item);} 
	@include sprite-selectors($map, $_item, $_item, $offset-x, $offset-y);
		@media only screen and (-webkit-min-device-pixel-ratio : 1.5), only screen and (min-device-pixel-ratio : 1.5)	{   
		@if (nth(sprite-position($map2x, $_item),2) != 2*nth(sprite-position($map, $_item),2) ) {
		}
                background-position: $offset-x round(nth(sprite-position($map2x,  $_item, 0, 2*$offset-y), 2)/ 2);
                background-size:image-width(sprite-path($map)) image-height(sprite-path($map));
		@include ph-sprite-selectors2x($map, $map2x, $_item, $_item, $offset-x, $offset-y);
	} 
}
/* Now we'll apply the ph-sprites mixing  to the .button class */
.button {
background: $tut-sprites;
	 @media only screen and (-webkit-min-device-pixel-ratio : 1.5), only screen and (min-device-pixel-ratio : 1.5) 
		{
			background:$tut-sprites2x;
		}
	@include ph-sprites($tut-sprites,$tut-sprites2x, button, true);
}

The above will compile to (note that two files have just been created : button-se4c41ebe77.png and button2x-s8a4e15a939.png)


.button {
  background: url('../images/button-se4c41ebe77.png');
  background-position: 0 -96px;
  height: 48px;
  width: 104px;
}
@media only screen and (-webkit-min-device-pixel-ratio : 1.5), only screen and (min-device-pixel-ratio : 1.5) {
    .button {
      background: url('../images/button2x-s8a4e15a939.png');
     } 
}
.button:hover, .button.button_hover, .button.button-hover {
    background-position: 0 0; 
}
.button:target, .button.button_target, .button.button-target {
    background-position: 0 -48px; 
}
@media only screen and (-webkit-min-device-pixel-ratio : 1.5), only screen and (min-device-pixel-ratio : 1.5) {
    .button {
      background-size: 104px 144px; 
    } 
}

The corresponding markup is as follows


<!DOCTYPE html>
<html lang="en">
<head>
<link href='http://fonts.googleapis.com/css?family=Homenaje' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Sansita+One' rel='stylesheet' type='text/css'>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sprites with Compass - tutorial</title>
<link rel="stylesheet" href="stylesheets/screen.css" type="text/css" media="screen" title="no title" charset="utf-8">
</head>

<body>
	<div>
		<h2>Sprites created with compass</h2>
		<a href="" type="submit" class="button" name="waiting">
		        Back
		</a>
		<a  href="#" class="button button-hover" title="hover">
		        Hover
		</a>
		<a  href="#" class="button button-target" title="clicked">
		        Clicked
		</a>
		<ul class="social">
			<li><a class="icons-twitter" href="http://twitter.com/pehaa">Twitter</a></li>
			<li><a class="icons-dribbble" href="http://dribbble.com/pehaa">Dribbble</a></li>
			<li><a class="icons-pinterest" href="http://pinterest.com/pehaa">Pinterest</a></li>
			<li><a class="icons-deviantart" href="http://pajkabajka.deviantart.com/">DA</a></li>
			<li><a class="icons-flickr" href="http://www.flickr.com/photos/pajkabajka/">Flickr</a></li>
		</ul>
		<p>Buttons by <a title="created in Vains Faktory by Joe Vains" href="http://vainsfaktrory.com" target="_blank">Joe Vains</a></p>
	</div>
</body>
</html>

Example 2 – social icons

We can use the same mixing for the simple social icons :


$icons: sprite-map("icons/*.png", $position: 100%, $spacing: 15px, $repeat: no-repeat);
$icons2x: sprite-map("icons2x/*.png", $position: 100%, $spacing: 30px, $repeat: no-repeat);
a {
    background-repeat: no-repeat;
    background-image:$icons;
    @media only screen and (-webkit-min-device-pixel-ratio : 1.5), only screen and (min-device-pixel-ratio : 1.5) {
      background-image:$icons2x;
    }
    @each $_icon in  twitter, dribbble, pinterest, deviantart, flickr {
	&.icons-#{$_icon} {
	  @include ph-sprites($icons,$icons2x, #{$_icon}, false, 7px, 7px);
	}
    }
}

4 Responses to “Standard and retina display sprites with compass – how to”

  1. Aetherpoint

    Aug 8th, 2012

    The only I don’t like about this that each individual icon class uses it’s own media query. It’s preferred to just switch out the background while keeping the poisitioning references the same.

  2. Thank you for very nice tutorial.

  3. Sandra Smith

    May 23rd, 2013

    Honestly speaking, this is far more complicated than what I have imagined. I am still struggling to get the hang on this Standard and retina display sprites but hopefully, I will be able to do this. Thanks for the share btw.

  4. Kilolo Jenkins

    Aug 12th, 2013

    Love the piccie but have no idea how you made it. LOL

    I would love to see a tutorial on how to make those buttons. I want to use something like that for a different project. :)

Leave a Reply