Source shuffling - responsive images based on media queries

There are lots of solutions to address the problem of responsive images. The solutions share a similar set of requirements, some meet the majority of the requirements, some don’t and specialise in doing one thing well.

I recently read a framework for discussing responsive images solutions by Jason Grigsby and his first point really hit home with me. Scaling a large image for mobile devices may not produce the desired results every time, content authors should be allowed to specify different images for different contexts.

Most solutions offer this approach including the picture tag proposal. Where this falls short, in my opinion, is that there is still a disconnect between the picture tag breakpoint values and the media query values specified in your CSS. Each time a picture tag is written you need to specify your breakpoints again. In a templating environment you would use variables to take care of this but you will still be setting your breakpoints in different places.

Then I recently read Jeremy Keith’s fantastic Conditional CSS article which helps connect your CSS media queries to JavaScript to perform actions based on breakpoints. The possibilities are endless with this little gem. One of those possibilities is using it for specifying breakpoints for responsive images that are set in our HTML.

Here’s the approach I am proposing:

<img src="mobilefirst.jpg" data-medium="tabletfriendlyversion.jpg" data-large="desktopfriendlyversion.jpg" />

We can store image URLs in data attributes to ensure they aren’t loaded initially, only the original src is loaded. This approach borrows from Josh Emerson’s Responsive-Enhance technique where you load the fastest image you can for mobile and replace on the fly where it’s suitable to do so for the end device. By combining this philosophy with Jeremy Keith’s Conditional CSS, then we have responsive images based on our media queries.

CSS:

@media all and (min-width: 45em) {
body:after {
    content: 'desktop';
    display: none;
}
}

As outlined by Jeremy’s Conditional CSS technique, this sets a string of text we can detect using JavaScript to determine when the media query is active. You would then repeat the above code for your other contexts eg “tablet”, you could extend it for a 2x retina display with the -webkit-device-pixel-ratio media query.

JavaScript:

var size = window.getComputedStyle(document.body,':after').getPropertyValue('content');

if (size == 'desktop') {
        $('img').each(function(index) {
            var large = $(this).data('large');
            $(this).attr('src',large);
            }
}
if (size == tablet') {...

So to recap on the code above, it’s checking that when the desktop media query is active by checking the existence of the content in the “body:after” element, then it swaps the src with the value of the data attribute “data-large”. And of course you would repeat this for the other media queries too. Note I’m using jQuery above, you (the smarter one in this dialogue) could do this without a framework, I’m just proposing an approach. Users without JavaScript enabled get the low resolution, compressed mobile image specified in the img src, I’m fine with this as users without JS are getting a lo-fi version of the web anyway.

So now we have an approach that ticks a few important boxes:

  • Breakpoints defined in CSS and only in the CSS
  • Images can be whatever the content author specifies (no automatic resizing)
  • No bandwidth hit for mobile devices
  • No code repetition
  • Fallback for users without JS

I would love to hear your thoughts on the approach above and any improvements you can think of.

View demo