Grails Retina Plugin 1.0 released

by Marc Palmer in

I have just released version 1.0 of the Retina plugin - a simple Grails plugin to help with rendering inline images with Retina support. This is a non-trivial area in which no single clean solution exists yet.

Site decoration, sprites, textures etc. that are currently rendered with CSS background-image are easy to handle without a tool like this plugin - just use media queries or any of the other retina CSS background-image techniques. (Don't get me started on how this is a total fail for web standards)

The inline image solutions are more tricky. You have concerns over whether you end up loading low and high resolution versions, flash of page with no inline images, and problems like URLs not being known until page build time i.e. supporting third party Avatars with retina resolution, or showing high resolution photo thumbnails in a gallery. Do you really want your contact sheet to download 100 low resolution images first, then another 100 high resolution?

Currently available techniques include but are not limited to:

  1. CSS background images using WebKit image-set. Requires rendering inline styles in your page and using <span> instead of <img> so it breaks semantics. However it auto-adapts to the display in use in multi-screen setups, and loads instantly as the page is parsed. Best user experience (except for accessibility)
  2. <img> tags that load the low resolution image (or none) at first, then have their src replaced by JavaScript code after the page loads. Semantic but means page takes longer to look complete. Best semantic HTML
  3. Serving images automatically using the correct resolution, depending on a cookie set in the client by JS to indicate whether retina is supported. Requires server-side smarts and arguably has issues with violating HTTP specification by returning different documents for the same URI. Best developer experience

This retina plugin currently supports approaches (1) and (2). You choose which is most appropriate for you for each image you render. Both involve compromise.

However it works, today. We're using it in the imminent next release of NoticeLocal.

Usage is simple. Instead of <img> or <r:img> you simply use the new <img:set> tag:

<!-- Using default mode, CSS background image -->
<img:set uri="/images/products/main.jpg" 2x="/images/products/main@2x.jpg"/>

<!-- Using JS mode -->
<img:set mode="js" uri="/images/products/main.jpg" 2x="/images/products/main@2x.jpg"/>

This simplicity belies some cool if slightly ugly things going on behind the scenes.

The CSS background-image mode (mode="bg", the default) requires Resources plugin 1.2.RC2 as a minimum. This is because it uses a new feature, <r:stash> to store the small chunk of CSS required to set up the background-image, so that this can be rendered cleanly in a single chunk in the <head> section of your page. This is like using <r:script> but supports JS or CSS (and maybe more in future).

The JS mode is simpler but still leverages the power of Resources plugin in order to pull in the small chunk of JS required, and the dependency on jQuery, in such a way that it loads at the right time and can coexist with your own resources.

Yes, the plugin requires jQuery and HTML5 currently. Perhaps we can rewrite the JS later to not require jQuery. However if you don't use jQuery you could always override the resource dependency on jQuery or override the imagesets.js resource to provide your own implementation. Or just fork, or whatever. Its not a huge amount of code.