The devices on which the web can be displayed increases everyday. Rather recently Apple released its new set of Macbook Pros with retina display, so called because at a normal viewing distance users wont be able to see any pixels. Designing for such a high resolution however, can become a little more tricky than you would expect.
Over the years the resolution of your average monitor has gotten bigger and bigger. If you’ve upgraded your monitor to a larger resolution but with the same physical monitor size you will probably notice that the elements (windows, icons, etc) will have reduced in size. That’s because the pixels are smaller, so if the window was 180 pixels wide, it now takes up 180 smaller pixels.
The sizing of elements on a retina display is adjusted for usability purposes
Now this may seem like not that big of a deal, but the pixels on the new Macbook Pros are so small that a window that is 180 pixels across will take up a really tiny amount of space. This has an impact on usability. How did Apple decide to combat this problem? They doubled everything in size. So your 180 pixel wide window now takes up 360 pixels. That’s why apps which haven’t updated for retina appear blurry and distorted, since they’ve just been doubled in size without taking anything else into consideration.
The object on the left takes up twice as many physical pixels
This means that a website will have to double in size to fit on a retina display. This brings us to the issue of device independent pixels. Since everything is doubled on a retina display, the pixel value you use in CSS will effectively cover double the distance on the screen. That means if you have width: 1000px;
, on a retina display you are saying width: 2000px;
.
If the web page wasn’t stretched to double its size things might look a bit weird. You can imagine it a little like in the image below.
On retina displays 1 CSS pixel is equal to 2 physical pixels in one directions
Images
Text and CSS elements resize perfectly, but we hit a little snag when we come across images. On a retina display, your 200×400 image is now 400×800. Of course, your image was never designed to be that size, so it stretches out of proportion and will probably look a bit blurry. How do we fix this?
Step 1: use icon fonts where possible
When possible, use an icon font. If you’re using images for fonts, chances are they won’t resize correctly to the dimensions you want. Icon fonts can resize to any size because they are effectively text. You can find a bunch of awesome icon fonts and why we use them in this recent post.
Step 2: Adjusting your images
The easiest way to adjust your images is to create an image double the size of whatever size you want (suppose you want an image that is 400×200, design your image as 800×400). Then half the size with the image tag.
<img src="image.jpg" alt="image" width="400" height="200" />
An image double the size is going to require a bit more time to download and this might not be in your or your user’s best interests. What are the best ways to do this?
Javascript? CSS?
Depending on your situation you might decide to use Javascript or CSS to fix your images.
Javascript
If you only have a few images on your website then Javascript might be the way to go. Double the size of all your images with your image editor of choice and then add the following jQuery code to your site: (which will half the image’s size)
$(document).ready(function() { $('img').each(function() { $(this).width(($(this).width()/2)); $(this).height(($(this).height()/2)); }); });
An alternative route to take (if you have a lot of images) is to have a class like .rr
which will represent all retina enabled images. Then you can add that class to images at your leisure and target it with the following jQuery:
$(document).ready(function() { $('.rr').each(function() { $(this).width(($(this).width()/2)); $(this).height(($(this).height()/2)); }); });
This works pretty well but it also requires larger images (twice the size of what you would normally use), and this of course requires better internet connections. On a mainstream website this might not be the best option.
Two Images with Javascript
Another way to do this with two separate images (i.e. one retina image and one non-retina image) is to add another attribute to your image and then use it to change the src with Javascript depending on screen resolution. For example you could do some HTML like this:
<img src="default.png" data-retina="defaulthd.gif" />
Then it’s just a case of halving the size and changing the URL with Javascript:
$(document).ready(function() { $(window).load(function() { $('[data-retina]').each(function() { var img = new Image(); img.src = $(this).attr('data-retina'); var width = img.width; var height = img.height; if(window.devicePixelRatio > 1) { $(this).width(width/2); $(this).height(height/2); $(this).attr('src', $(this).attr('data-retina')); } }); }); });
Clear cut HTML
All of this can be accomplished with a simple image tag where you half the width and height of the image. That’ll make everything look sharp on a retina display. Again, you just need one image, double the size of what size you want it to appear in the webpage. Then just half the size with the width and height attributes. The image below should be 1000×1600, which is then halved.
<img src="image.jpg" width="500" height="800" alt="" />
CSS
Our second option is to use CSS. For this method to work we need to create 2 versions of the same image, one doubled in size, and one in the size you wish to display.
The first method we are going to use is to check the resolution using some media queries. These are still experimental in webkit, so they use browser prefixes.
Targeting the resolution
This is pretty straight forward, you just need to have a media query in your CSS targeting the correct resolution. Since these are a little new we do have a bit of writing to do, but it will target the correct resolutions all the same:
@media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (in-resolution: 2dppx) { /* CSS goes here */ }
So what exactly are we going to put inside our media query? Well as you might’ve expected we have to use background images, which might not be to everyone’s tastes. This is probably the easiest way to ensure compatibility though. Using the example below where we have ih.jpg and ihx2.jpg, we would do something like this:
.ih { /* On a regular screen */ background: url('images/ih.jpg'); background-size: 120px 120px; /* The image is going to be 120x120 */ height: 120px; width: 120px; } @media only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (in-resolution: 2dppx) { .ih { background: url('images/ihx2.jpg'); /* We don't need to define background-size, but it's here to remind you that the background size will stay the same */ background-size: 120px 120px; height: 120px; width: 120px; } }
You could use this method with our x2 picture without the media queries if the image was an acceptable size. For instance, ignoring the problem of internet connections we could just do this:
.ih { /* On retina and normal screens */ background: url('images/ihx2.jpg'); background-size: 120px 120px; /* The image is going to be 120x120 */ }
HTML5?
There are some interesting developments on the HTML5 front too. Of course, you can’t really use this yet. The current HTML5 specification talks about a srcset
attribute for images which will pick an image based on many factors which the browser will choose from. There has been some controversy over this however. For instance, you could write something like this:
<img src="image.jpeg" srcset="imagehd.jpeg 2x, imagemobile.jpeg 700w, imagemobilehd.jpeg 700w 2x" alt="The Image" />
The above will use the image.jpeg image by default, and then adjust to the imagehd.jpeg if there are two device pixels per CSS pixel (that’s a retina display). The 700w means that the screen is a max of 700 CSS pixels across, so the second image is for mobiles, and the 3rd for retina mobiles.
Many have pointed out that this means web designers will have to learn a new format for images where media queries would do the job, but this method also has some benefits. For example, this method means the browser can take a lot of factors into consideration, including web connection, location, etc, to choose which picture is best for the user. Although some might like to adjust it, this is the current standard.
Best Strategy
To summarize, the strategies that I’ve presented today are as follows:
- 1 large image halved with HTML
<img>
tag - 1 large image halved with CSS background-size
- 1 large image halved using Javascript
- 2 images, one large, one small, differentiated with CSS media queries
- 2 images, one large, one small, differentiated with Javascript and HTML
Your best strategy is to pick and choose which method you want to use on a website by website and picture by picture basis. Some images can stretch without anyone really noticing, others cannot. My current method when implementing images is to design an image double in size and see what size in kb it comes out at. If it’s too big I’ll consider implementing a non-retina image, or using two versions of the image. You have to adjust your method depending on a number of variables, and that comes down to you!