Responsive web design (RWD) redefined the way we design web sites. The disruption went deep and changed both the processes and deliverables we hold dear for so long, and today in Netgen RWD is the default route we take with every new project. But, bundled with its core unifying principle of every browser getting the same HTML, some tough problems arose. One of the most discussed is the responsive images problem of providing alternate images based on device capabilities and screen sizes.
The problem
There is a number of reasons we need responsive images, and although being the most obvious one, the sheer speed of downloading binary image data is not the only one. As much as the speed of mobile networks grows, there will allways be bad reception areas where a state of art gadget becomes a sluggish brick crawling the web on EDGE or GPRS speeds, and we need to work harder to provide the user the best experience possible in such occasions.
This issue is also being actively discussed within W3C, but as we wait for the standard to emerge (and for browser vendors to implement it) there are workarounds we can start using even now.
The solution
One of the better known JavaScript-based solutions for responsive images is Picturefill by Scott Jehl (Scott works in Filanment Group, an agency involved in building sites such as Boston Globe) and is available at https://github.com/scottjehl/picturefill It is a pollyfill replacement for the proposed picture element that uses HTML <span> elements to define different image sources suited for specific media queries.
The way it works is that once the page is loaded, JavaScript is used to replace the appropriate span element with an img tag that loads the most suitable image source. HTML used by picturefill is pretty self-explanatory:
<span data-picture data-alt="A giant stone face in Angkor Thom, Cambodia">
<span data-src="small.jpg"></span>
<span data-src="medium.jpg" data-media="(min-width: 400px)"></span>
<span data-src="large.jpg" data-media="(min-width: 800px)"></span>
<span data-src="extralarge.jpg" data-media="(min-width: 1000px)"></span>
<!-- Fallback content for non-JS browsers. -->
<noscript>
<img src="small.jpg" alt="A giant stone face in Angkor Thom, Cambodia">
</noscript>
</span>
Our main idea was to combine picturefill with the standard eZ Publish image alias system so that different versions of images are created automatically (as image aliases) and wrapped up as picturefill HTML blocks. The solution is packaged as a standard eZ Publish extension - ngresponsiveimages. Kudos to our Mario Ivančić as the lead extension developer, and the code is available at https://github.com/netgen/ngresponsiveimages
To use ngresponsiveimages, there are 2 things the developer needs to do:
1. In template code, the attribute_view_gui function, when used with ezimage attributes, needs to be called with a responsive_image_class parameter. This parameter triggers the “responsivity” and defines which set of image versions (coupled with media queries) will be used (explained in detail below)
{attribute_view_gui attribute=$node.data_map.image responsive_image_class=imagefull}
2. In the ngresponsiveimages.ini file the mapping of responsive_image_class parameter to media queries and appropriate eZ Publish image aliases is defined. This is set in two separate setting groups.
First we specify the media queries that will be used for picturefill setup:
# Bootstrap 3 grid breakpoints: xs (phones), sm (tablets), md (desktops), and lg (larger desktops)
MediaQueryExpressions[]
MediaQueryExpressions[xs_x1]=(min-width: 480px)
MediaQueryExpressions[xs_x2]=(min-width: 480px) and (min-device-pixel-ratio: 2.0)
MediaQueryExpressions[sm_x1]=(min-width: 768px)
MediaQueryExpressions[sm_x2]=(min-width: 768px) and (min-device-pixel-ratio: 2.0)
MediaQueryExpressions[md_x1]=(min-width: 992px)
MediaQueryExpressions[md_x2]=(min-width: 992px) and (min-device-pixel-ratio: 2.0)
MediaQueryExpressions[lg_x1]=(min-width: 1200px)
MediaQueryExpressions[lg_x2]=(min-width: 1200px) and (min-device-pixel-ratio: 2.0)
Secondly, we define the mapping between responsive_image_class values and eZ Publish image aliases, for a given media query:
[imagefull]
DefaultMap=i450_3_2
MediaQueryMappings[]
MediaQueryMappings[xs_x1]=i450_3_2
MediaQueryMappings[lg_x1]=i900_3_2
We also need to define eZ Publish image aliases that are used by the ngresponsiveimages extension. As with all the standard image aliases, this is done in image.ini:
[i450_3_2]
Reference=original
Filters[]=geometry/scalewidthdownonly=450
[i900_3_2]
Reference=original
Filters[]=geometry/scalewidthdownonly=900
With this example configuration in place, for screen sizes narrower than 1200px (targeted with MediaQueryExpression labeled as xs_x1), 450 px image alias is used (image alias i450_3_2), and for screens wider than 1200px (MediaQueryExpression lg_x1) the downloaded image would be 900 px wide (image alias i900_3_2).
It is important to note that these are example values and that the real settings will depend on the layout and design of the site you are building. Also, bear in mind that in most responsive sites, the actual width of the image is not defined with its pixel size, but with the width of the container element the image resides in.
The results
The improvements to page speed that can be achieved with this extension vary depending on the type of the content we are serving on the site. It makes sense to expect that better results on image-heavy sites such as news portals, gallery sites and similar.
Our experiences with using picturefill are very positive. On a recent project (a frontpage of a typical news site), we managed to reduce the image traffic from 1.3 MB on desktop size screens to 578 KB on mobile. Also, we tested the solution on a wide range of mobile and desktop platforms and it worked great with no side-effects detected.
Future improvements
As ngresponsiveimages is an extension build around a pollyfill, it will cease to be needed as soon as a standard based solution for responsive images is agreed on and becomes widely supported by browsers. As much as we would like for this to happen soon, there is a lot of work that needs to be done on the standardization part, even before the vendors implement the final solution.
The improvement we are looking into first is to implement the solution as an eZ Publish 5 bundle, although even now it can be used on eZ 5 installations as a legacy extension.
Hope you find this extension useful, happy coding!
Links
https://github.com/netgen/ngresponsiveimages
https://github.com/scottjehl/picturefill
http://timkadlec.com/2013/11/why-we-need-responsive-images-part-deux/
https://medium.com/p/d9a47f94eea7 - an alternative approach for responsive images ;-)
Comments