Grow your CSS skills. Land your dream job.

#133: Figuring Out Responsive Images

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 small screens and the full size of the browser window on large screens and 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.

Comments

  1. This is some great progress with the spec finally being implemented.
    I guess I’m kind of more curious in how CMS’s will allow for it to be utilised and as to whether clients will grasp the idea.
    Least i know that I can start using this stuff on my recent projects with the polyfill.

    • I managed to get this working in WP for 3 different sized header images for large, medium and small viewports.

      Just add three image sizes to your functions.php like so:

          add_image_size('lrg-hdr', 1170, 544, true); 
      add_image_size('med-hdr', 750, 400, true); 
      add_image_size('sml-hdr', 500, 325, true); 
      

      and then for the template drop this in:

      <img
      srcset=" 1170w,
      750w,
      500w”
      sizes=”100%”
      alt=””>

      Thanks for another great tutorial.

  2. Jak

    Hadn’t heard about srcset until now but it looks great. Plus, I can take still use this on client projects by letting the CMS take control of creating/ loading multiple versions of their image. Awesome!

  3. Nice as always..Chris!

  4. We’re going to need a cookbook for this stuff haha

  5. Great overview Chris (following on from Erics’ overview on his own site and Smashing recently).

    I can feel new brain synapses developing with each pass over these articles and each new codepen. Hoping to have a list of patterns for this as part of going over it time and time again, if you’re working on something similar give me a shout and I’ll send my stuff through.

  6. Erik Isaksen

    Thanks for all of your hard work in the community. Great Work!

  7. Fred Campbell

    Hi Chris, what happened to your Rode Podcaster mic, sounds like you’re sitting in an oil barrel!

  8. Ian Mustafa

    The third code block should be like this:

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

    You missed the img selector in @media

    • Good catch! Dumb me. I probably had it nested but then moved it out because that’s a weird thing to chuck in there in an article like this so moved it out and forgot how that works derp.

  9. Michael

    Is this method only compatible with newer browsers? There doesn’t seem to be any mention of compatibility, is it safe to assume this works with all browsers then?

    • Jesse

      The <picture> element and the srcset attributes are currently not supported by any browsers without the help of a polyfill. The Picturefill polyfill included in the list of links at the end will enable both features in most browsers. The Picturefill website has more detailed documentation about browser support.

      Now that the spec is being nailed down, it is likely that the major browsers will begin supporting it natively in the near future.

  10. I will use this in my current responsive website project.

    Tested this on windows IE7, IE8, IE9 and works just fine.

    I need to see how it will work on with a responsive Slider

  11. Samuel Larsson

    Great walkthrough! Only on question left: If we provide the browser with detailed “sizes”-info (max/min-widths + relative widths)—would there be no or little use for “2x” in srcset? Does this solution handle resolution when picking images, even without the 2x-thing?

    • The most important advice is use EITHER the w values OR the x values, never both.

      Good:

      srcset="image.jpg 1280w"
      srcset="image.jpg 2x"
      

      Bad:

      srcset="image.jpg 1280w 2x"
      
  12. It’s good to see the final solution includes a backward compatible img implementation, the picture element was always going to be an issue with legacy browsers.

  13. Permalink to comment#

    As Mr Burns would say “Excellent”.

  14. Its excellent solution, instead of using the img{width:100%;} in CSS

  15. Justmeotw
    Permalink to comment#

    Not particularly current nor maybe even relevant to this thread, but I’ve been using the code below for a few years and get the desired results ( in this case responsive bg images)
    In the WP header file
    $(document).ready(function() {
    // DOES NOT ALLOW FOR HEIGHT RESIZE USING MEDIA QUERIES UNTIL YOU ADD THE NEW BACKGROUND-SIZE RULE
    bgImageTotal = 11;
    var randomNumber = Math.round(Math.random()*(bgImageTotal-1))+1;
    imgPath=(‘/img/backgrounds/bg_’+randomNumber+’.jpg’);
    $(‘#wrapper’).css({
    ‘background’ : ‘url(“‘+imgPath+’”) 100% 0 no-repeat’,
    ‘background-size’ : ’100%’
    });
    });

    The key rules in the #wrapper ID in style.css
    position: relative;
    background-color:#222;
    width: 100%; height: 100%;
    max-width: 1280px; max-height: 800px;
    margin: 1% auto; padding: 1% 0;

    This seems to get it right and because I’m using jQuery IE8 even plays nice (with, to be fair, the default use of css3-mediaqueries.js)

    That being said, I’m sure I could learn a thing or two from taking a look at these new standards.
    Peter

  16. Jan

    Maybe a stupid question but I am little bit confused. So in the future I can use BOTH responsive image solutions?

    picture and friends
    img with srcset and sizes

    At the moment “picture” seems more logical to me but I think like Martin Wolf I need some time get it.

  17. trCreative Web Design Cheshire

    Nice technique using srcset, a new one to me, thanks.

  18. I want to use it on eshop with one simply rule – display low-res images on mobile phones and better quality ones on desktop. I asume that with doing this, i could save quite a lot of data trafic for mobile users so as a sideeffect increase for them the speed of the web. I know that mobile phones now got sometimes very high resolution, so i would like to target lowress displays the low resolution displays or higher with some multiplier. And i would like to do it with shortest possible html code with only 2 img versions (btw i heard that e.g. jpg with compression lvl 60 resized to 50 % is nicer and data-smaller, then nonresized quality 90 jpg) and with good enough compatibility (most used android, ios, winphone mobile browsers). What code would you reccomend?

Leave a Comment

Current day month ye@r *

*May or may not contain any actual "CSS" or "Tricks".