#133: Figuring Out Responsive Images

Avatar of Chris Coyier
Chris Coyier on (Updated on )

📣 Freelancers, Developers, and Part-Time Agency Owners: Kickstart Your Own Digital Agency with UACADEMY Launch by UGURUS 📣

I’m probably a bit rare in that I rather enjoyed trying to keep up on the responsive images thing. It’s an interesting problem that bred lots of interesting solutions. The whole thing is starting to wrap up now though, now that the official solutions are:

  1. <picture> and friends
  2. <img> with srcset and sizes

The problem is: I don’t really get it. I thought the original picturefill was pretty simple. List a bunch of sources with media queries. First one to match, that’s the one that gets used. That made sense to me. This new stuff is a bit different. Solvable problem! Just like everything else, I need to figure it out.

That’s why I was totally sympathetic to Martin Wolf’s post “The new srcset and sizes explained” where he was struggling with the same problem. So I used that as a starting point for playing myself.

Here’s some things I’ve learned.

This part makes sense I think:

And this is where it gets weird:

Why are we telling the browser how big an image is? It’s a browser. Shouldn’t it know how big images are? Well it does, but only after it downloads the entire thing. Smarter decisions can be made right off the bat if it knows this information sooner. So we just tell it. You could lie. Or be wrong. Which is weird but whatever.

Now that you have those different image options listed, you’ll need to also use the sizes attribute. This provides more information to the browser about what how to intend to display the image so it can use that in making a choice about which.

The sizes attribute is also weird to me:

Again, it’s all about intention. The sizes attribute doesn’t actually have any effect on the size the image actually renders. (I was wrong about that in the video.) That’s left to the real actual image size itself, or (more likely) CSS. We’re just giving the browser information about what we intend to do so that it can make a smart choice about which image to use. I think it feels weird because we could again, lie, or be wrong, which is unusual thing in HTML.

If we provide all this info, and do it right, then the browser (or the polyfill) can make a smart choice about what image to display.

Let’s take this example:

<img
  src="small.png"
  srcset="large.png 1280w,
          medium.png 640w,
          small.png 320w"
     sizes="100%"  
     alt="whatever">

Say we’re on a 1x screen. Because we’ve told the browser we’re going to be using these images as big as we possibly can (100% of the viewport), the “breakpoints” for when the browser will flip out the images will happen at 1280px, 640px, and 320px, the same exact sizes as we’ve told it the images are.

If we’re on a 2x screen, those “breakpoints” will cut in half (regardless of what we actually do to size those images) and will be at 640px, 320px, and 160px.

Now let’s say we use the same images, but we know a lot more about our page layout, and used something like this:

<img
  src="small.png"
  srcset="large.png 1280w,
          medium.png 640w,
          small.png 320w"
     sizes="(max-width: 500px) 250px, 500px"  
     alt="whatever">

Here we’re saying (with the sizes attribute), if the viewport is 500px or smaller, we intend to display the image at 250px wide. If the viewport is wider than that, display the image at 500px wide.

That would match up with CSS like this:

img {
  width: 500px;
}
@media (max-width: 500px) {
  img {
    width: 250px;
  }
}

On a 1x screen, you’ll always get the 320w (small) image when the viewport is 500px wide or smaller, and you’ll always get the 640w (medium) image when the viewport is larger. You’ll never get the large image, because it can tell you’ll never need that many pixels.

Ona 2x screen, you’ll always get the 640w (medium) image when the viewport is 500px wide or smaller (because it thinks it needs 500px of pixels and the small isn’t enough at 320px), and you’ll always get the 1280w (large) image when the viewport is larger. You’ll never get the small image, because it’s never enough pixels to cover what you’ve told it you intend to render the image at.

Actual Sizing

Remember the actual sizing of the image is still up to you. I would think in the majority of cases it’s you doing it through the CSS. And the CSS always wins. What you declare there will be the rendered width of the image no matter what happens with the srcset and sizes stuff. That just works out which image will be shown.

This is what makes the sizes attribute a bit tough. Let’s say you have something like:

.container {
  width: 80%;
}
.container article {
  width: 50%;
}
.container article img {
  width: 33.33%
}

That’s not unusual at all. So now what size do you use in the sizes attribute? That would be 13.33% (multiply them all together) because that number needs to be relative to the viewport, not the container. And that doesn’t take into account margins and padding and stuff on those containers, so it’s kind of a guess. I guess close counts in horseshoes, hand grenades, and the sizes attribute.

Then let’s say a media query comes along and the body actually becomes 75% wide on top of all that. You need to know that so you can adjust what you think the rendered size of the images will be. Your sizes attribute might become:

sizes="(min-width: 500px) 8%, 13.33%"

Then go through that again for every layout media query you have that affects content images. It can get a bit complex.

Practical Sizes?

I suspect most real world usage will use something like:

<img
  src="small.png"
  srcset="large.png 1280w,
          medium.png 640w,
          small.png 320w"
     sizes="(min-width: 500px) 50%, 100%"  
     alt="whatever">

Assuming that content images will be around half the size of the browser window on large screens and the full size of the browser window on small screens – just let the breakpoints happen where they happen. You’ll still get a pretty decent choice this way without slaving away over matching all your media queries exactly.

And remember these are content images. HTML tends to last longer than CSS or JS does, especially when it’s content.

Other Things To Know

You can also specify if an image is 2x or 1x with srcset. So a really simple use case can be:

<img srcset="small.jpg 1x, large.jpg 2x"
   src="small.jpg"
   alt="whatever" />

That alone is pretty useful. Don’t mix it with specifying widths. As Eric Portis says:

And again let me emphasize that while you can attach 1x/2x resolution descriptors to sources in srcset instead of w descriptors, 1x/2x & w do not mix. Don’t use both in the same srcset. Really.

And remember when I said the original picturefill was easy? The new <picture> can be that easy, but the <source> elements inside <picture> also support srcset and sizes. That means you can get very specific. It adds another layer to this:

  1. You decide which gets displayed based on media queries
  2. That has it’s own srcset/sizes, for determining exactly which src gets used

Here’s an example from Eric Portis’ recent Smashing Magazine article:

<picture>
   <source media="(min-width: 36em)"
      srcset="large.jpg  1024w,
         medium.jpg 640w,
         small.jpg  320w"
      sizes="33.3vw" />
   <source srcset="cropped-large.jpg 2x,
         cropped-small.jpg 1x" />
   <img src="small.jpg" alt="A rad wolf" />
</picture>

Links

See the Pen srcset & sizes testcase by Chris Coyier (@chriscoyier) on CodePen.