HTML5 Responsive images in your CMS

Published on September 19, 2016 by Christopher Zimmermann



Responsive web design is everywhere, so why is it still so hard to implement responsive images on a website? I’m talking about images in responsive grids (via media queries, bootstrap, foundation, etc) where container widths change based on the width of the browser - and images resize to fill those widths.

No, it’s not hard to add the width:100% and height:auto css styles to get the img element to scale to fill the layout properly.  It is hard to load the correct image file to match that width. Which image should you load: a small image file that will look pixelated or blurry on a desktop screen, or a large image file that eats up bandwidth and loads slowly on a mobile phone?

 

HTML5 to the rescue?

People have worked on this problem. And now the HTML5 spec and most browsers support their solution: the new srcset and sizes attributes of the img element (and the more complex picture element). These attributes specify a set of image sources, and when the browser should use them. Done, right? Umm..... unfortunately not.

The hitch is the “when the browser should use them” part.  

For a responsive image in a 960px width grid, the syntax can look like this:

 

<img src="/img/whale.jpg"

srcset="

/img/whale-160.jpg 160w,

/img/whale-240.jpg 240w,

/img/whale-320.jpg 320w,

/img/whale-480.jpg 480w,

/img/whale-640.jpg 640w"



sizes="

(min-width: 960px) 160px,

(min-width: 480px) 240px,

320px"

/>

 

The srcset attribute specifies the set of image files to use, and how wide each of them are.

The sizes attribute specifies which size of image the browser should load for each width of the browser. If you don’t include the sizes attribute, then the browser loads the image that most closely matches the width of the entire browser window. This is useful for a full width hero image, but otherwise not what we are looking for.

In this example - the sizes attribute specifies, if the browser is wider than 960, use an image that is 160px wide (because my grid has 6 columns). If the browser is wider than 480, use an image that is 240px (because my grid has 2 columns). Otherwise fallback to a 320px wide image (because my grid goes down to 1 column). The srcset also specifies 480 and 640 pixel width images, because the browser can intelligently use these on high pixel density displays.

 

Impressions

So - how do you like that markup? The srcset attribute makes sense, but I don’t like the sizes attribute. First of all, why should I even have to specify sizes - the browser knows how wide the image element is in the page. And secondly, the layout information should be in the css, not the html. With the sizes attribute, I always need to ensure that my html and css are in sync, and that is not good.

As a CMS developer, this makes me cringe because it’s so important for us to make placing images easy for website developers - and for content authors. Simple markup and avoiding duplication are important: this could affect a developers choice of CMS. These days componentization and modularization are the objectives of web developers - it should be easy to use a component anywhere.

The reason that you need to specify sizes is another browser speed optimization: the browser starts loading all of the images in the page as soon as it discovers their url in the html. At this early stage - the css and js have not been loaded, the page has not yet been rendered, and the final presentation width of the images is not yet known by the browser. So it’s a case of performance optimizations getting in the way of performance optimizations.

Given this bigger picture, how should HTML support responsive images? It’s a balance between the loading time, and total bandwidth, the quality of the image presentation in the website, and the cleanliness and ease of coding. I wish that the default behaviour in the absence of a sizes attribute was to defer image loading until the document was ready, so that the correct image could be loaded based on the actual image dimensions. I hope the w3c will specify a new attribute to the img element enable this behaviour - perhaps “autosize” is a decent name.

 

Javascript to the rescue!

It turns out that there are javascript libraries that do just that. Imager.js from the BBC web crew, and lazysizes from Alexander Farkas and contributors are popular ones. I especially like lazysizes as the markup is still based on img tags, its implementation relies on the native browser srcset implementation if it’s available, there are many plugins to enhance its behaviour, and it continues to be in active development.

https://github.com/BBC-News/Imager.js/

https://github.com/aFarkas/lazysizes

Using lazysizes, the markup becomes:

 

<img src="transparent.jpg"

data-srcset="

/img/whale-160.jpg 160w,

/img/whale-240.jpg 240w,

/img/whale-320.jpg 320w,

/img/whale-480.jpg 480w,

/img/whale-640.jpg 640w”

data-sizes="auto"

class="lazyload"

/>

 

It is easy to support browsers with javascript disabled too.

 

CMS

This topic came up while working on the new demo website for Magnolia, and it's a question that we get from a lot of our customers. I'm happy to have something that we can recommend now. Imagine working on a CMS project where you want to use a simple image component. It’s a lot of extra work, management, and potential errors if the developer or content author always needs to configure the image based on how it should change size every time you use it! With the lazysizes approach you can configure the available set of sizes for the images just once for your project - and the browser will do the work of choosing the right image every time, even if you re-style your site layout later.

 

Example

Of course each img element still needs to have the srcset attribute with the available versions of that source. But it's easy to create a server script to generate these for every image. For example, in Magnolia with freemarker templating:

 

   [#assign sources = [

       {"name":"240-wide", "width":"240"},

       {"name":"320-wide", "width":"320"},

       {"name":"480-wide", "width":"480"},

       {"name":"960-wide", "width":"960"},

       {"name":"1366-wide","width":"1366"},

       {"name":"1920-wide","width":"1920"}]]

 

<img src="/images/transparent.gif" alt="${alt}"

  data-srcset="

   [#list sources as src]

       [#assign rendition = damfn.getRendition(asset, src.name)!]

       ${rendition.link} ${src.width}w,

   [/#list]

   "  

class="lazyload" data-sizes="auto"

/>

 

This example takes advantage of Magnolia’s out-of-the-box imaging system to automatically generate the resized image files we require based on a single high resolution asset stored in the DAM. The srcs array contains the name of the configured variations in Magnolia.

You can see this in action on the Magnolia demo list pages. Open the Chrome browser devtools and go to the network panel - and resize the browser width. You’ll see it load in just the right sized image for the current browser width.

 

 

Magnolia Module

I've created a Magnolia module that provides a responsive image component.

https://github.com/topherzee/responsive-image-component

Or available on npm as responsive-image-component

Give it a shot!

 

Conclusion

HTML is evolving to keep up with the reality of how it is actually used on new devices and platforms, as it should. But I hope the principle of simplicity that made HTML so approachable (and therefore successful) does not get lost along the way. While I support markup that empowers developers to present just the right image, it’s rather complicated. To be fair - they cover other use cases than just resizing responsive images, such as “art direction”. Still, there should be a simple markup that does the right thing to cover the primary use case - for example the “autosize” attribute I suggested above.


I’d like to hear your thoughts on this. What do you think about the default behaviour? Do you have other approaches to responsive images that you think are better than lazysizes?



Comments



{{item.userId}}   {{item.timestamp | timestampToDate}}

About the author Christopher Zimmermann

Christopher is a software developer and startup enthusiast with a focus on front-end web technologies. His interest in innovative UI leads to diverse experimentation from phone based GPS services, to Oculus Rift immersive experiences. He organizes the 'basel.js' javascript meetup group.


See all posts on Christopher Zimmermann

Demo site Contact us Free trial