Responsive Images Revisited—The Picture Element

Responsive images have some challenges beyond the best pixel density to display for a given screen. There are issues maintaining the correct aspect ratio and overall image hierarchy. There are also issues with important details being lost as an image gets much smaller than the original.

Closeup of picture frames

Last week we looked at the srcset and sizes attributes and how browsers use the values we provide to determine which image is the best to display given the pixel density of image and screen or the size of the image and viewport.

One key point is that browsers make the decision. It’s based on your guidance, but it’s still the browser’s decision. In most cases that’s a good thing. Unfortunately browsers can’t tell when details are lost or when image hierarchy is changed. Sometimes we need to make the decision.

Enter the picture element, where we get to decide. Today I want to walk you through the picture element, it’s associated elements and attributes, and how to work with them. I’ll follow up with some thoughts for when to use srcset and when to use picture depending on your use case.

How the picture Element Works

As I mentioned above, when using srcset you provide browsers with information they use to make an informed decision. With the picture element you provide rules that the browser must follow.

That means the browser won’t be able to optimize things quite as well, but sometimes it’s more important to serve a different image regardless of any performance loss.

Here’s a simple example for how you might use the picture element to switch between three images.

1
2
3
4
5
<picture>
  <source media="(min-width: 50em)" srcset="large.jpg">
  <source media="(min-width: 30em)" srcset="med.jpg">
  <img src="small.jpg" alt="My image">
</picture>

There are three html elements in the code above. The picture element wraps a couple of source elements and an img element.

The img element is an ordinary img tag with src and alt attributes and nothing you haven’t seen before. This will be the default image that’s shown to browsers without support for the picture element and it’s the default condition for browsers that have support.

The source elements aren’t new. They come from the audio and video elements. In the code above each source element has two attributes. One, srcset, is exactly what we saw last week. In this code each srcset attribute is pointing to a single image, though it can point to more. The media attribute works like a media query.

The first source element in the code above sets the condition (min-width: 50em) for which large.jpg should be used. The second line is similar. It sets a different condition (min-width: 30em) for when to show med.jpg. If neither condition applies then small.jpg (the default) is displayed.

It’s important to note that browsers have to use the first source tag that matches. Say the above code was written as below with the two source tags presented in the opposite order.

1
2
3
4
5
<picture>
  <source media="(min-width: 30em)" srcset="med.jpg">
  <source media="(min-width: 50em)" srcset="large.jpg">
  <img src="small.jpg" alt="My wonderful image">
</picture>

With the code written this way, a browser open to 60em would use med.jpg because 60em is more than 30em and viewport width and condition match. The browser must use this source and it ignores the rest even though large.jpg is the better image to show. The decision to show the images comes with some responsibility.

Also note that an img tag with a src attribute needs to be included or none of this works. You can’t use source elements alone to cover all conditions.

To help browsers render pages a little quicker you can specify image dimensions for each media query in your css. This will help with performance as it’s one less calculation for browsers to make.

1
2
3
4
5
6
7
8
9
img { width: 10em height: 10em }

@media (min-width: 30em) {
  img { width: 20em; height: 15em; }
}

@media (min-width: 50em) {
  img { width: 30em; height:20em; }
}

You can also style the picture and source elements, but be aware they may not do what you expect automatically. The image below is a screenshot of an example I created to test the picture code above with two source elements and an img tag.

Screenshot showing borders around the picture, source, and img elements

I set a 3px border on each of the three elements, picture (olive), source (orange), and img (red). I added a little padding and margin to picture and source as well to make it easier to see the borders and I also set both the picture and source elements to display: block. This makes it easier to distinguish where one begins and another ends.

You can see that picture is wrapping both source elements and the img element. Even though the image you see displayed was chosen from one of the source elements (the second one), you can see it’s being displayed inside the img element. Keep that in mind when styling with css.

You might have noticed I haven’t yet explained the picture element itself. That’s because all it really does is contain the source and img tags. There’s not much more to the element.

You aren’t limited to a single image in each srcset. The srcset attribute works the same here as I described last week. You can add multiple images and specify their pixel densities as follows.

1
2
3
4
<picture>
  <source media="(max-width: 50em)" srcset="large-1x.jpeg, large-2x.jpeg 2x">
  <img src="small" srcset="small-2x.jpeg 2x" alt="My image">
 </picture>

The code above directs browsers to choose an image from the source tag for devices up to 50em wide and to choose an image from the img tag when the viewport is wider. In either case browsers would then choose the more appropriate image based on the pixel density of the device and images.

The type Attribute

The source element can also take a type attribute to serve different image formats to different browsers. For example Google is promoting a new WebP image format, which is also supported by Opera. Other browsers don’t support this image type. Microsoft is promoting it’s own image format, JPEG XR (.jxr), which again other browsers don’t support

Here’s how you might set up the picture element to show different image formats to different browsers.

1
2
3
4
5
<picture>
  <source type="image/webp" srcset="image.webp">
  <source type="image/vnd.ms-photo" srcset="image.jxr">
  <img src="image.jpg" alt="My type of image">
</picture>

In the code above, browsers supporting WebP will use image.webp. Browsers that don’t support WebP, but do support vnd.ms-photo (JPEG XR) will use image.jxr All other browsers will use image.jpg.

You probably won’t be experimenting with new image formats just yet, but it’s not hard to envision a future where you might.

When to Use picture and When to Use srcset

In the article that was the inspiration for this series, Jason Grigsby offers two use cases for responsive images and I’ve added a third.

  • Resolution switching
  • Size Changes
  • Art direction

Size changes is the case I’ve added, since you can use the sizes attribute to serve variable width images without difference in their pixel density. Andreas Bovens offers four questions to ask and answer before choosing which method to use.

  • Do I want my image sizes to change depending on my responsive design rules?
  • Do I want to optimize for high-dpi screens?
  • Do I want to serve different art depending on certain contextual factors?
  • Do I want to serve images with different mime types to browsers that support them?

The first two are easily handled by srcset and it’s the method you should generally use unless you answer yes to either of the last two questions, which require the picture element.

Most of the time you’ll want to let browsers decide on the image and so you’ll use the srcset attribute. Browsers can better optimize for performance that way. It also allows browsers to take user preferences and network connectivity into account.

Hopefully browsers will build in more sensors (with associated attributes we can set) so that we can provide more information to help with their decision. Even if they don’t give us attributes they can already do things like detect slow bandwidth and serve a lower resolution image, despite a pixel dense screen.

Art direction is the use case where you need to be in control of the decision. It’s the least common use case of the three, so you should be using the picture element in practice less often than srcset. In practice we’re going to use srcset and sizes more than picture, source, and type.

Closing Thoughts

Where the scrcset attribute helps browsers make an informed decision about which image to show, the picture element gives you control. You set the conditions for which image should load and browsers must obey.

The obvious pro is the control the picture element gives you. Sometimes you can’t leave the decision to browsers. Browsers can’t see the detail or lack of detail in an image. They don’t understand or attempt to maintain your image hierarchy across devices and conditions.

The downside is a performance hit. Browsers can optimize the decision when they make it so they can grab the “best” image quicker than when they have to do what you tell them.

These pros and cons suggest you want to use srcset most of the time. The majority of image switching you’ll do will likely fall into the use case of swapping high and low resolution images and srcset is better suited for that task.

In the case of art direction where you absolutely need a specific image to load under specific circumstances you need to use the picture element and accept any performance hit from doing so.

Next week I want to continue. In creating a demo (for the week after next), I started thinking about what makes a high resolution image, high resolution and while looking for an answer I discovered a few myths about resolution, dpi, and ppi. I also have some general thoughts about responsive images to share.

Download a free sample from my book, Design Fundamentals.

Leave a Reply

Your email address will not be published. Required fields are marked *