Photograph of a man running along a pier during sunset, with a harbour in the background

After a long, long period of debate and competition, web development finally has the markup and syntax it needs for responsive bitmap images: the <picture> element, together with the srcset and sizes attributes. And, as of today, we have fully implemented support for <picture> in two browsers – Chrome and Firefox – together with an excellent, well-tested polyfill for others.

There is a fair amount of confusion regarding these new features. Type HTML5 picture into Google and it will likely suggest HTML5 picture vs. srcset, implying that they are competing standards. That’s not true: <picture>, srcset and sizes are most effective when used together. If you need to distinguish them, a decent summary is:

  • <picture> is used for art direction
  • srcset determines resolution and pixel density in images
  • from this variety of options, sizes is used to set the optimum image for the current area

While they work great together, these components are easiest to learn separately; so, in the spirit of my Flexbox for Designers series, I’ll tackle <picture> first, followed by srcset and sizes in subsequent articles.

picture: Because Not All Fluid Images Are Created Equal

The fluid images concept has been around a long time, but comes with a price: not all images look equally good scaled down. This is particularly true of larger illustrations that have a strong central focus: Comparison of an unchanging responsive image at different viewport sizes

Below a certain viewport size, the runner photograph in the example above looks out of proportion next to body text, with important details lost. Ideally, we want an easy way to use different versions of the image at different viewport widths.

There have been a number of solutions to achieve this over the years, from my own SVG technique to Estelle Weyl’s related “clown car” option. Most use some degree of JavaScript hackery. With <picture>, we have a built-in, native, browser-supported solution:

<picture>
	<source srcset="runner-wide.jpg" media="(min-width: 1000px)">
	<source srcset="runner-narrow.jpg" media="(min-width: 500px)">
	<img src="runner-square.jpg" alt="Photograph of a man running along a pier at sunrise, with a harbour in the background">
</picture>

There are a few things to notice immediately:

  1. The <picture> element is very similar in concept to the <audio> and <video> elements: it is a tag that surrounds alternative media sources.
  2. Each of these media sources points to an image with an associated inline media query that contains the conditions under which it will appear.*
  3. A standard <img> tag is the last required element. It functions as a proxy for the <source> elements to display their content; it also acts as a fallback for browsers that don’t understand <picture> and don’t run the polyfill described below.
  4. The alt value of the image acts as a description for the entire set.
  5. Source order matters. The inline media query in the <source> element immediately above the smallest square image in the example translates to “the viewport must no wider than 500 pixels before it can display runner-square.jpg”, with wide-aspect photos taking commiseratively greater values.
  6. The example takes a mobile-first approach, with runner-square.jpg loaded and displayed by default if the polyfill is not applied.

You can see the result of <picture> in the header image for this article: resize the browser to see the art-switching process.

picture Polyfill

The best solution for browsers that don’t yet support <picture>, sizes or srcset is Scott Jehl’s excellent picturefill, which has been in development during the entire protracted debate. It is open source, framework-free, has automatic support detection, and is easy to add to the <head> of any document:

<script src="picturefill.js" async></script>

The async attribute makes the script non-blocking, allowing the rest of the page to load while it works. There’s one addition required: like <main> and other elements in older browsers (particularly IE), the client needs to be informed of the very existence of the picture element via JavaScript:

<script>document.createElement( "picture" ); </script>
<script src="picturefill.js" async></script>

Dealing With IE9

With the polyfill in place, you can use <picture> on your page as shown above, with or without srcset and sizes. However, IE9 needs a little more work, by fooling it with a pseudo-<video> element with a conditional comment:

<picture>
	<!--[if IE 9]><video style="display: none;"><![endif]-->
	<source srcset="runner-wide.jpg" media="(min-width: 1000px)">
	<source srcset="runner-narrow.jpg" media="(min-width: 500px)">
	<!--[if IE 9]></video><![endif]-->
	<img src="runner-square.jpg" srcset="runner-square.jpg" alt="Photograph of a man running along a pier at night, with a harbour in the background">
</picture>

That’s it: between the polyfill and native support in Chrome and Firefox, you now have art-directed images in all modern browsers.

CSS & Correct Context

As with the <figure> element, it’s important to understand that you shouldn’t use <picture> around every image on your site. Early estimates suggest that less than 25% of images require art direction: if a photograph just needs resolution and size switching, you should use srcset and sizes inside a single <img> element. I suspect that the majority of images to benefit from <picture> will be “hero” banner images, making the associated CSS very easy:

picture img {
	width: 100%; height: auto;
}

Note that there is no need to style the <source> elements, as they are set to display: none; by default.

Application Support

The demand for multiple versions of image assets will place more stress on image editors and their tools. While the requirements of srcset and sizes has decent support in Adobe’s recent Generator offering, as well as Sketch, I haven’t found good application support for the new demands of <picture>. For now, creating art-directed alternatives will likely continue to be a process of establishing guides and cropping out different versions of the same image. A photograph being prepared in PhotoShop with cropping

However, Generator and other tools are increasingly based on open technologies such as Node, so the possibility exists for a plugin to be developed that will streamline this process: when I have the opportunity, I’ll see what I can hack together. (And if you’re aware of other options, please let the community know in the comments below!)

* While it’s not widely appreciated (and sadly, removed from the spec, at least for now) the <video> element also supported inline media queries.

Photograph of António Lopes by Pedro Moura Pinheiro, used under an Attribution-NonCommercial-ShareAlike 2.0 Generic Creative Commons license.