Using CSS Sprites to optimize your website for Retina Displays

  • Categories:

CSS Sprites have been around for a while now. Matter of fact, they have been around for over eight years. They allow for some great monetary and bandwidth optimizations for medium and large websites, and they allow for a better experience on the user’s side, since there is a reduction in load time.

For a lot of small websites, the extra layer of complexity that CSS Sprites brings to your assets was not always worth it. But with high-resolution screens, such as Retina Displays, becoming more and more prevalent, there is a new big reason to use them.

Optimizing for High-Resolution Screens

To optimize for high-resolution screens, you have to add a reference to a larger image asset within a specific media query. So for every image asset you have at a normal resolution (or @1x) you have to add a high-resolution version (@2x). This means double the amount of files, and double the amount of selectors and file references in your CSS.

Yes, there are ways to take this on with Javascript, but I honestly don’t see depending on another external piece of code as a forwards-looking solution. By using a CSS Sprite, you only need to override the link to the @1x sprite file for all the selectors that include high-resolution assets.

In the next case we have four selectors with image assets. First, we have a snippet you would get use all of your assets would be separate. Then we have a snippet that uses a sprite of which the non-retina version is 200 pixels wide and 200 pixels high:

Retina CSS Sprite Example, via miekd.com. Icons used are from the bijou iconpack by visualidiot.com.

span.location {
	background: url(location.png) no-repeat 0 0;
}

span.success {
	background: url(success.png) no-repeat 0 0;
} 

a.delete {
	background: url(delete.png) no-repeat 0 -100px;
} 

.content a.fav-link {
	background: url(favorite.png) no-repeat 0 0;
}

@media only screen and (-webkit-min-device-pixel-ratio: 2), 
only screen and (min-device-pixel-ratio: 2) {
	span.location {
		background-image: url(location@2x.png);
		background-size: 16px 14px;
	}

	span.success {
		background-image: url(success@2x.png);
		background-size: 13px 14px;
	}

	a.delete {
		background: url(delete@2x.png) no-repeat 0 -100px;
	} 

	.content a.fav-link {
		background-image: url(favorite@2x.png);
		background-size: 11px 13px;
	}
}
span.location {
	background: url(sprite.png) no-repeat 0 0;
}

span.success {
	background: url(sprite.png) no-repeat -100px 0;
} 

a.delete {
	background: url(sprite.png) no-repeat -100px -100px;
} 

.content a.fav-link {
	background: url(sprite.png) no-repeat 0 -100px;
}

@media only screen and (-webkit-min-device-pixel-ratio: 2), 
only screen and (min-device-pixel-ratio: 2) {
	span.location,
	span.success,
	a.delete,
	.content a.fav-link {
		/* Reference the @2x Sprite */
		background-image: url(sprite@2x.png);
		/* Translate the @2x sprite's dimensions back to 1x */
		background-size: 200px 200px; 
	}
}

Sidenote: bear in mind that the example used is just a media query based on the guidelines of iOS and Retina MacBook Pro’s. Android actually uses different values min-device-pixel-ratio, depending on the screen that’s on your device.

In short:

  1. Instead of referencing to all your assets as separate files, put them in a sprite.
  2. Create a 2x version of your asset sprite that is exactly double the size, and has the different assets at exactly double the dimensions inside of the sprite.
  3. Gather all the selectors that reference the sprite, and reference them towards the @2x sprite within the media query for high-resolution displays.
  4. Make sure you don’t forget to set the background-size property to translate the dimensions of the @2x sprite.

Mind you, this is just an example of three image assets. Now think about what this means when you extrapolate this over all of the assets on your website. Not only do you save network requests and stylesheet file-size, the whole process of creating retina assets gets way more efficient and cleaner code-wise.