Images are notoriously one of the most challenging aspects of responsive web design. Today we'll look at how the <picture> element, a solution to the problem of responsive images, can be used right now.
First, the Problem
The days of fixed-width, pixel perfect website design are well and truly behind us. In the present day of widescreen monitors, internet TVs, multiple sized tablets and smart phones our designs now have to cater for everything from 320px wide up to potentially as high as 7680px wide.
Along with this multi-resolution landscape comes a need for images to stretch or shrink to fit these wildly varying requirements. This can prove to be something of a problem given that, with the exception of vector graphics, the vast majority of images have specific pixel based widths that do not change.
So what do we do?
The Current, Most Common Solution
As a general rule, you'll find the following in just about any responsive site's CSS:
This code uses the max-width: 100%; setting to ensure images never go beyond the width of their parent container. If the parent container shrinks below the width of the image, the image will scale down along with it. The height: auto; setting ensures the images' aspect ratio is preserved as this occurs.
It solves the problem in one respect, allowing us to display the same image under many different circumstances. But it doesn't allow us to specify different images for differing circumstances.
A New Solution: <picture>
<picture> is a new element which is set to become part of HTML5.
It will bring the process for placing responsive images up to speed with the way the current <audio> and <video> elements work. It will allow you to place multiplesource tags, each specifying different image filenames along with the conditions under which they should be loaded.
It will allow you to load an entirely different image depending on:
Media query results e.g. viewport height, width, orientation
This in turn means you can:
Load appropriately file sized images, making the best use of available bandwidth.
Load differently cropped images with different aspect ratios to suit layout changes at different widths.
Load higher resolution images for higher pixel density displays.
How Does <picture> Work?
The basic steps of working with <picture> are:
Create opening and closing <picture></picture> tags.
Within those tags, create a <source> element for each query you want to run.
Add a media attribute containing your query on things like viewport height, width, orientation etc.
Add a srcset attribute with the corresponding image filename to load.
Add extra filenames to your srcset attribute if you want to provide for different pixel densities, e.g. Retina displays.
Add a fallback <img> element.
Here's a basic example which checks if the viewport is smaller than 768px, then if so loads a smaller image:
You'll notice that the syntax used in the media attribute is the same as you might be used to from creating CSS media queries. You can use the same checks, meaning you can query max-width, min-width, max-height, min-height,orientation and so on.
You can use these checks to do things like loading landscape or portrait versions of an image depending on device orientation, and you can still mix in size queries at the same time. For example:
<sourcesrcset="smaller_landscape.jpg"media="(max-width: 40em) and (orientation: landscape)">
<sourcesrcset="smaller_portrait.jpg"media="(max-width: 40em) and (orientation: portrait)">
<sourcesrcset="default_landscape.jpg"media="(min-width: 40em) and (orientation: landscape)">
<sourcesrcset="default_portrait.jpg"media="(min-width: 40em) and (orientation: portrait)">
The above code loads a smaller, landscape cropped version of the image on a smaller, landscape oriented device. It loads a larger version of the same image on a larger landscape oriented device.
If the device is portrait oriented it loads a portrait cropped version, at small size on a small device or at large size on a large device.
If you want to provide different resolution versions of your images for higher density displays, you do so by adding extra filenames to the srcset attribute. For example, let's look at our first snippet of code from above with handling for Retina's 2x resolution added:
The media query is still evaluated first so you can control the dimensions your image will appear at on screen. Then the display's pixel density will be checked and if higher densities are both supported and allowed by the user's preferences, the higher density version of image will be loaded.
Using <picture> Today
Right now native implementation for <picture> is in the works for Chrome, Firefox and Opera. In the future it's likely we'll see widespread support as other browsers also catch on. But for the moment that support is still yet to arrive.
In the meantime, you don't have to wait if you'd like to start using <picture> right now. You simply have to use Picturefill 2.0; a polyfill provided by those clever folks atFilament Group.
After downloading the picturefill.js file to your project it can be implemented by simply loading it in your site's head section:
There is also an option to load the script asynchronously for added efficiency, which you can read about in Picturefill's documentation.
With this script loaded, the <picture> element will work as I've explained, with only a few limitations.
Picturefill works just fine with other IE versions, however IE9 doesn't recognise source elements that are wrapped in picture tags. To get around this, conditionally wrap your source elements in video tags which will then make them visible to IE9, for example:
Like IE9, Android 2.3 can't see source elements inside a picture element. However, it can understand the srcset attribute when used on a regular img tag. Be sure to always include your fallback img element with the default filename in the srcset attribute for Android 2.3 and any other browsers that may have the same issue.
The other requirement Picturefill has is for native media query support, to enable the queries in the media attribute to work. All modern browsers support media queries, with IE8 and lower being the only non-supporting browser with a small remaining user base.