Optimizing Graphics for Apple’s Retina Display Using the CSS Background Size

I’ve written previously about how to optimize graphics for the Retina Display.

In one of the examples I said that you could use a media query to target high resolution devices and then provide a high resolution graphic for just those devices.

While it ends up being extra work to produce multiple graphics I believe it is the best option for the end user, as it provides a sharp graphic at an optimal file size.

In order to accomplish this we would need to use a new property called background-size. I didn’t go into detail on how to use it then, but if you’ll hang with me for just a bit I’ll introduce you to the practice.

And, as a bonus, I’ll show you how to use the background-size property with high-res images sprites.

What Is the Background Size Property?

The background-size property was introduced in the CSS3 spec and as its name infers, it allows you to specify the size of an element’s background image.

There are several values you can use to specify the size.

You can use pixels. The first value sets the width, the next the height. If you only specify one value it will set the other to auto.

div { 
    background: url(logo.png) no-repeat;
    background-size: 150px 150px;
    height: 200px;
    width: 400px;
}

You can use a percentage. When using a percentage the background is sized relative to the width of the container. So in the example below, our container is 400px wide and our background size is set at 25% so the graphic should be 100px wide.

div { 
    background: url(logo.png) no-repeat;
    background-size: 25% auto;
    height: 200px;
    width: 400px;
}

The cover value will scale the image proportionately to completely fill the content area.

div { 
    background: url(logo.png) no-repeat;
    background-size: cover;
    height: 200px;
    width: 400px;
}

The contain value will scale the image proportionately to it’s maximum width and height, while keeping the whole image contained in the container.

div { 
    background: url(logo.png) no-repeat;
    background-size: contain;
    height: 200px;
    width: 400px;
}

Using Background-Size with Retina Displays

As I’ve written previously, when Apple introduced the Retina Display they didn’t want everything on the screen to appear smaller in comparison to other devices of the same physical size. The icons on the home screen of the iPhone 4 should be the same physical size as the icons on the home screen of the 3G S.

In order to accomplish that, graphics on the Retina Display are doubled in size. With the pixel doubling images can appear soft or blurry if they are not optimized for this pixel doubling.

To counteract this what we’ve done is to create a graphic at twice the size we want it to display it and then set the background-size property to display the graphic at the preferred size.

For example if I want my logo to fit in a container that is 100px by 100px, I’m going to create and upload a graphic at least twice that size.

In our example the logo is actually 500px by 500px. If I use this logo as a background image for my container — and I don’t specify a background-size — the image will just fill the container and eventually be cropped at the container’s edges.

div { 
    background: url(logo.png) no-repeat;
    height: 100px;
    width: 100px;
}

Obviously, we only want to use this large resolution image on our high resolution devices so we’ll use a media query to constrain its use to only devices that have a pixel ratio of 2.

We’ll also set the background-size at 100px by 100px. We’re still using a high resolution image that is actually 500px by 500px, but our CSS declaration allows the browser to resize it down to 100px by 100px.

@media screen and (-webkit-min-device-pixel-ratio: 2), screen and (max--moz-device-pixel-ratio: 2) {
div { 
    background: url(logo.png) no-repeat;
    background-size: 100px 100px;
    height: 100px;
    width: 100px;
}
}

Now, you could run into some issues with the downsizing if your graphics have lines that are 1px in width, but you’ll have to experiment with each individual case.

What About Sprites?

Could I create one image sprite that has both normal resolution and high resolution graphics?

You could, but then you’re still making your user (on a slower connection) download a larger graphic. You would also be forced to declare a background size for your normal graphics which wouldn’t be compatible with IE browsers before version 9.

You would still be better off creating both high resolution and low resolution sprites. That sounds like a lot of work, but it’s really not. In my workflow I create the high resolution sprite and then reduce the image size by half and save a copy. When the image needs to be updated, just work with the high-res version and again, save a copy at half the size.

So how do I specify a background-size of 32px when my sprite is 300px? All it takes is a bit of math which I’ve worked out for you. Here’s your initial process …

For our example we’ll use some social icons. I want them to display at 16px by 16px. We’ll create our Retina sprite at double my intended size (32px by 32px) and then save it. I’ll then cut the image size by half and save the low-res version. I’ve placed both sprites below.

Now for the initial CSS. The background-position values are not technically needed since they’re zero, but I’ve placed them there for your reference.

#social-icons .facebook {
    background: url(images/social-sprite.png); 
    background-position-x: 0px; 
    background-position-y: 0px;
    height: 16px;
    width: 16px;
}

#social-icons .facebook:hover {
    background-position-y: -16px;
}

For our Retina display we have to declare a media query that will specifically target the Retina display. We also declare the background size rather than the width and height.

So, the million dollar question is, how do I know what to use for the background size?

Here’s the formula:

high res image width / target image width = x
original sprite width / x = background-size width

Our high res social icon is 32px wide.
Our target image width is 16px.
The total width of our sprite is 96px.

32/16=2
96/2=48

And one last thing. Since we’ve only calculated the width, we’re going to set the height to “auto” to ensure our graphic scales correctly. You can do it the other way around, but I’ve found it’s easier to use the width.

@media screen and (-webkit-min-device-pixel-ratio: 2), 
screen and (max-moz-device-pixel-ratio: 2) {
    #social-icons .facebook {
        background: url(images/social-sprite2x.png); 
        background-position-x: 0px; 
        background-position-y: 0px;
        background-size: 48px auto;
    }

    #social-icons .facebook:hover {
        background: url(images/social-sprite2x.png); 
        background-position-x: 0px; 
        background-position-y: -32px;
    }
}

Again, there are multiple ways of accomplishing the same thing we’ve demonstrated here, and there are pros and cons for each. I think — for right now — using media queries and high resolution sprites are your best options.

I hope this tutorial gives you some more useful ammo for your css arsenal.

Comments

  1. Hey Josh,
    You did a great job explaining a confusing subject, but how about creating a Genesis plugin that would do it for us :-). The plugin could add info boxes to the media info screen where you would upload or point to the low-res image and input the target image width.

  2. Thanks, Josh. Great tutorial.

  3. This is something interesting and you have perfectly explained this topic. Expecting a plugin from you :)

  4. Great tutorial but for the less techie, a WordPress plugin is a great idea. I hope you give it a shot. Thanks. :)

  5. Hi Josh,

    Thanks for this write-up. If I do not own a retina display device is there a way to virtually confirm/check to make sure it is successful?

  6. Great Post Josh!
    Thanks for posting.

  7. Interesting read, I am seeing lots of apps which look real bad on the new ipad, and now with the new mac book retina this is a must.

  8. Thanks nice post! I will be implementing it int he future! :)

  9. It works perfect, with the exception of IE8 and earlier. They don’t know about “background-size”…:-(

    • IE8 also doesn’t understand @media queries correctly either:

      “@media screen” is ok
      “@media screen and …” will be ignored

      Graceful degradation is the key.

  10. Nice mini tut, just tried this today

  11. Thanks Josh, the part about @2x CSS sprites was doing my head in. The best part is, if you generate your @2x sprite just right, I guess you shouldn’t need to adjust any of the background-positions…?

    • Yep – in fact you might be able to just generate one graphic and not have to use sprites depending on how it degrades on a normal screen.

  12. Thomas Manoj :

    Thank you very much… a wonderful article on Retina display hi-res images. I had only little knowledge of background-size css attribute, but now I am clear about.

    Thanks a lot for you good work and God bless you :)

  13. Highly descriptive post, I loved that bit. Will there be a part
    2?

  14. any chance of getting the inline html code for this?