If you’re interested in adding retina image support to your website or app and you happen to be using SCSS, here’s a handy shortcut that makes it dead simple.
Just include this SCSS mixin in any rule and specify the high resolution image’s path and device independent pixel dimensions. Here’s an example:
div.logo {
background: url("logo.png") no-repeat;
@include image-2x("logo2x.png", 100px, 25px);
}
Putting the high resolution alternative in the same rule as the normal image instead of putting it in a separate @media query rule or in different stylesheet altogether is a big win in terms of clarity in our CSS. It’s easier to read, easier to understand and easier to maintain.
The image-2x mixin detects high resolution displays like this:
@mixin image-2x($image, $width, $height) {
@media (min--moz-device-pixel-ratio: 1.3),
(-o-min-device-pixel-ratio: 2.6/2),
(-webkit-min-device-pixel-ratio: 1.3),
(min-device-pixel-ratio: 1.3),
(min-resolution: 1.3dppx) {
/* on retina, use image that's scaled by 2 */
background-image: url($image);
background-size: $width $height;
}
}
The mixin not only makes development easier, but it centralizes the definition of high resolution displays so it can easily be changed globally as more devices come to market. The @media query is based on the one in Thomas Fuchs’ Retinafy book but, for example, we’ve already modified it to define the Google Nexus 7 with it’s 1.3 pixel ratio as a retina-capable device.
A key insight here is that with SCSS, @media queries can be nested inside other rules. Such that these both work the same:
@media print {
body {
// print styles
}
}
body {
@media print {
// print styles
}
}
Purists of hand-crafted CSS may rail that this method results in the rule being repeated in the compiled CSS each time we use this mixin and that’s true. Basecamp is light on images so we’re talking about a handful of repetitions, not dozens or hundreds. It also seems likely that as SCSS continues to improve, these rules will be smartly combined. For now that trade-off is worth the improved clarity and convenience in our code.

Jason Z. wrote this on Oct 11 2012 There are 16 comments.
Aaron Jensen 11 Oct 12
You can get media query combining right now w/ sprockets using https://github.com/aaronjensen/sprockets-media_query_combiner
Brandon 11 Oct 12
I created a Gist for lazy LESS CSS version of this awesome mixin! https://gist.github.com/3874012
Richard Lyon 11 Oct 12
You can get the combination fairly easily by having the initial mixin simply append to a list (selector + image) and have a final mixin which spits out a combined list.
This does, however, cause the issue of your cascade breaking.. but that will happen anyway if #116 gets implemented.
River 11 Oct 12
Even better, if you’re using Compass, you can automatically add dimensions to your mixin.
Compass image dimensions helpers
Even better than that, though, is you could just feed it the image names and have it handle everything else.
2X Mixin
If you wanted to get very fancy you could write a function to take one image name, parse it, and generate the 2x image name automagically. That way, you’d only ever need to pass one image name into the mixin.
mezza 11 Oct 12
V cool
Phil Ricketts 11 Oct 12
Works great.
Modified above gist work: https://gist.github.com/3874841
ali 11 Oct 12
Another solution for Compass users is this helper and mixin which automatically applies the retina CSS if a file is found with a ’@2x’ suffix. It also takes advantage of the image-width helpers that the above comment mentions.
http://blog.joelambert.co.uk/2012/03/09/compass-sass-mixins-for-simple-retina-images-on-websites/
Mark 12 Oct 12
I find the scaled down versions of the images work well enough so I just use the 2x versions by default and combine them all in a sprite, even for non-retina.
You only need to fall back to 1x for browsers that don’t support background-size (ie)
https://gist.github.com/3877549
ADI 12 Oct 12
Great post, you should do more on coding.
James 13 Oct 12
Slightly off topic, but what IDE is that? I’m really loving that color scheme on the text.
Johnny 14 Oct 12
I’ve just found this on your blog: http://37signals.com/svn/posts/93-its-the-content-not-the-icons
Funnily enough, nowadays you have the “tweet” and “like” buttons on your blog. How does it hold with the post mentioned above?
Fadi E. 14 Oct 12
Hi Jason,
I wonder whether that code in the CSS is actually valid CSS .
The way I would do it is I would a condition in the PHP code to include that CSS (instead of doing it across the board).
website design bangalore 15 Oct 12
A critical thing to remember is that this can become very inefficient if you are using it in many places, as sass’s implementation currently just pulls the media query repeatedly out of the current selector, rather than looking through and combining media queries into blocks for each resolution.
Janmejai 17 Oct 12
thanks,helped me a lot.
George Morris 17 Oct 12
Exactly why we created RetinaJS – http://retinajs.com
Boris Kaiser 17 Oct 12
And here’s my solution with Compass: https://gist.github.com/3908158
This discussion is closed.