Ever since Ethan Marcotte started talking about responsive web design in 2010, developers and designers have been scrambling to find ways to deal with the issue of responsive images. It’s a thorny problem because we’re serving the same website, with the same image sources, across a wide range of device widths. Do you want a blurry, pixelated image on a large display? Or do you want to load a huge (but oh-so-pretty) image on your mobile phone? Talk about being stuck between a rock and a hard place.
Loads of smart people, namely the Responsive Issues Community Group (RICG), have been working together to solve this conundrum. That’s why the
picture element and the
sizes attributes are being drafted into the HTML 5.1 specification. Because we cannot predict where and how users are going to view our websites, we need the browsers themselves to pick the best image for the situation. The new specification will address the following issues:
- Device-pixel-ratio-based selection
- Viewport-based selection
- Art direction-based selection
- Image format-based selection
The spec also introduces two new attributes—
srcset lets us declare a set of image sources, which browsers will serve according to certain conditions we specify using descriptors.
x descriptors indicate the pixel-density of the images, while
w descriptors indicate the width of the images; the browser will use this information to pick the appropriate image from the list. The
sizes attribute provides the browser with some context on the size of the image element to be displayed, and must be included when using
w descriptors. This is especially relevant for variable-width images, which I’ll discuss in detail later.
The point is, we now have the option of serving images of different quality or art direction depending on the user’s viewport, without some complicated server-side setup. Responsive images will become part and parcel of the HTML specification; eventually, all browsers will support this solution.
Now, let’s take a tour of the selection types and how you can make them work for you.
Fixed-width images: device-pixel-ratio-based selection
With the introduction of Retina screens, it became necessary to take into account not only screen resolution but also pixel density. Retina screens, 4K displays, UltraHD—all of these have way more pixels packed into them than a standard resolution display of the same size. More pixels = sharper image quality.
If, for some reason, you have an image that will always display at a certain width regardless of screen size—the site logo or a profile image, say—then device-pixel-ratio-based selection is the way to go. The browser will choose which image to load based on its device-pixel ratio.
srcset attribute basically lists the pool of source images from which the browser can pick to load. It’s a comma-separated list. The
x descriptor indicates the device-pixel ratio of the image. Depending on what environment the browser is operating in, it will utilize this information to select the appropriate image. Any browsers that don’t understand
srcset will simply load the image declared in the
<img srcset="crest-383.jpg 1.5x, crest-510.jpg 2x" src="crest-255.jpg" alt="USWNT crest" />
An example of a fixed-width image might be a site’s logo, which remains the same size regardless of viewport width. Content-related images, however, are usually responsive; their sizes tend to change depending on the viewport. For those types of images, there’s a better method.
Fluid-width images: viewport-based selection
For fluid-width images, we use
srcset with the
w descriptor and
w descriptor tells the browser the width of each image in the list. The
sizes attribute is also a comma-separated list containing two values. As of the latest specification, if the
srcset has any images using the
w descriptor, then the
sizes attribute must be present as well.
There are two values in the
sizes attribute. The first is a media condition. The second is the source-size-value, which determines the width of the image given that particular media condition. One important thing to note is that you can’t use percentages as the source-size-value; the only relative CSS length you can use is
<img srcset="uswnt-480.jpg 480w, uswnt-640.jpg 640w, uswnt-960.jpg 960w, uswnt-1280.jpg 1280w" sizes="(max-width: 400px) 100vw, (max-width: 960px) 75vw, 640px" src="uswnt-640.jpg" alt="USWNT World Cup victory">
Here, I’m telling the browser that for viewport widths up to 400 pixels, make the image 100% of the viewport width. At viewport widths up to 960 pixels, make the image 75% of the viewport width. And for everything above 960 pixels, make the image 640 pixels. If you’re unfamiliar with
vw, take a look at Tim Severien’s great article explaining viewport units.
The browser utilizes the information from
sizes to serve the image that best matches the stated conditions. If my browser’s viewport is 600 pixels, it would most likely display the image at
75vw. The browser will try to load the first image larger than 450 pixels, which is
uswnt-480.jpg. If I’m on a Retina display with a device-pixel ratio of 2, then the browser will try to load the first image larger than 900 pixels, which should be
uswnt-960.jpg. We can’t be certain of exactly which image will be served because each browser has some leeway in how their algorithm picks an appropriate image based on the information we provide. This is what “viewport-based selection” means.
Because the first two examples display the same image at different quality levels, the
srcset attribute alone is sufficient. Again, if you’re worried about legacy browsers, that’s what the
src is for—those browsers will just treat it as a regular image and load from
src. If you want to show slightly different images at different widths, for example, showing only the critical parts of an image at smaller widths, then use the
picture: art direction-based selection
picture element is like a wrapper for the image and its sources. Browsers still need
img to recognize that an image needs to be served; without
img, nothing will render at all.
source provides the browser alternate versions of the image to display. Art direction-based selection is used for situations when we want a specific image to display at a specific breakpoint. There is no ambiguity in terms of image selection when you use the
<picture> <source media="(min-width: 960px)" srcset="ticker-tape-large.jpg"> <source media="(min-width: 575px)" srcset="ticker-tape-medium.jpg"> <img src="ticker-tape-small.jpg" alt="USWNT ticker-tape parade"> </picture>
In this example, when the viewport is larger than 960 pixels, a landscape-oriented version of the image (
ticker-tape-large.jpg) is loaded. For widths larger than 575 pixels, the browser loads a cropped portrait-oriented image (
ticker-tape-medium.jpg) instead. And for widths smaller than 575 pixels, the image (
ticker-tape-small.jpg) loaded has been cropped to focus just on one player.
picture element is backwards compatible; browsers that don’t support the
picture element will display
img as usual. All standard attributes for images, like
alt, should be applied to
source: image format-based selection
A number of new image formats have come into existence in recent years. These new image formats offer better quality at lower file sizes. Sounds good, right? Until you realize that none of these formats is universally supported across all browsers. Google’s WebP performs very well, but is only natively supported by Chrome and Opera. JPEG-XR, originally known as HD Photo, was a proprietary image format released by Microsoft, supported only by Internet Explorer. If you want to learn more, Zoltan Hawryluk wrote an in-depth examination of these new formats.
<picture> <source type="image/vnd.ms-photo" src="wwc2015.jxr"> <source type="image/jp2" src="wwc2015.jp2"> <source type="image/webp" src="wwc2015.webp"> <img src="wwc2015.png" alt="WWC 2015"> </picture>
source also has a type attribute, by specifying the MIME type of each image, browsers can choose the first source that has a type attribute of a supported MIME type. The order of the source matters, in this case, but if the browser doesn’t recognize any of the image types, it will just fall back to the original
Can I use all this right now?
At time of writing,
picture is supported by the latest stable releases of Firefox, Chrome, and Opera. Safari and Internet Explorer do not support
picture natively at all.
srcset fares slightly better, with full support on the latest stable releases of Firefox, Chrome, and Opera, and partial support on Safari 8 and Internet Explorer Edge, where they allow for use of the
x descriptors for resolution switching, but not the
Quite a few polyfills out there address this support problem. The most well-known is probably Scott Jehl’s picturefill. I currently use Alexander Farkas’s respimage on my own site. We’ve finally reached a point where we’ve agreed on a solution for how to deal with responsive images, and that solution is getting implemented across all major browsers. Even though the specification is still being refined, we’re really close to a native responsive solution.