Retina & Responsive Images

Grav empowers you to deliver Retina and HiDPI images

6 mins

Going through the issues on the Grav GitHub repository, I noticed someone was asking for Grav to support Retina images and immediately thought this is my chance to start contributing to this awesome project. As per suggested in the comments, I quickly came up with a solution using the new srcset attribute that has been making its way into HTML5. There are various different versions, but we chose to go with the most recent one. Before I go into more detail on our solution, I will provide a bit more background on the issue.

Background information

As I mentioned, there was a request to support Retina images. To understand about Retina images, you first need to understand about pixel density. In this case, pixel density is the amount of pixels fit on a certain part of your screen, measured most commonly in pixels per inch. However, you might also know pixel density from your scanner at home, that 150dpi or 300dpi setting you can choose really means, "how many dots (or pixels) should the resulting scan contain for every inch of paper I'm scanning?" So now that you know how pixel density works, we can move on to Retina displays, basically a Retina display has double the pixel density of a regular display. This means your 100 pixel wide image would cover only half the amount of space it would on a normal browser, which in turn results in your image being stretched out to cover the space it normally would. The result of this is that your image does not look as nice anymore! This is where so-called Retina images come into play, they're larger (double the size) versions of your existing images.

Okay, so you're using Retina images now because you want them to look good on those fancy Retina displays, but you're also concerned about bandwidth. You don't want your users with regular displays to have to download these large images to display your website. The folks that are working on HTML5 have crafted an elegant solution to this problem, namely the srcset attribute for the image tag. Without going into to much technical detail, what it does is provide you with a place to say: "Hey browser, here is a list of possible images in various sizes, you pick the best one and download it!" The greatest aspect about this srcset attribute though, is that it does so much more than adding support for Retina images. This is because it does not only take into account the pixel density of your screen, but also it's general size. For example, your iPhone 5 has a Retina screen but its display is also significantly smaller than a desktop Retina display. With the srcset implementation, your iPhone's browser might decide to use the regular image instead of the Retina image if it's large enough and save you some bandwidth.

One last thing, Retina is actually a brand name by Apple to indicate these types of higher density displays, but they are not limited to Apple at all. Lots of manufacturers manufacture high density displays. You can think of them as @2x displays, indicating they have double the density. There are even triple density ( or @3x ) displays out there (Apple calls them Retina HD.)

How Grav helps you out

With the latest release, Grav will automagically start adding this srcset attribute to your image tags! This capability is provided by Grav's unique media functionality.

Just provide a Retina image in your page folder and Grav takes care of the rest. It will even generate the smaller images for you if you only provide the Retina, triple density (Retina HD) or even higher density images.

How do I use this awesome feature you ask? Well it's simple, there's only one thing you need to remember and that is how you need to name your images.

.
├── default.md
└── sample@3x.jpg

As you can see on the image above, you simply need to add an @[number]x suffix to your image name where the number indicates the pixel density this image is meant for. You can go as high as you want here, there are no limits. Another cool feature is that if you only provide sample@3x.jpg, Grav will automatically create sample@2x.jpg and sample@1x.jpg for you! If you want more control, you can also provide the smaller images yourself, just follow the naming convention and Grav will use those instead of generating the images. And that folks, is all there is to adding support for Retina displays with Grav!

Need an example? Well here you go:

If I have an image that is 2200px wide and I save it as sample@3x.jpg in a page folder. Then in the page file default.md, we simply reference the standard image name of sample.jpg in regular markdown format:

![My Responsive Image](sample.jpg)

and the corresponding HTML output would look a little something like this:

<img src="/images/cache/sample1x.jpg" alt="My Responsive Image" srcset="/images/cache/sample1x.jpg 733w, /images/cache/sample@2x.jpg 1466w, /images/cache/sample@3x.jpg 2200w" sizes="100vw" />

As you can see, the srcset contains three version of my image, all accompanied by their respective widths so the browser will know how large they are and can make an intelligent choice on which image to retrieve.

And here it is live:

My Responsive Image

If you have a retina display, you should see the best resolution image. If you adjust the width of your display, and reload the page, your browser should grab the best appropriate image for your browser size.

Browser support

So here's the thing, I also have some bad news for you. At this point in time, only recent versions of Chrome (and Chrome Mobile) and Opera support this srcset attribute. But fear not, legacy-browser-loving friends of mine! I have also created a Srcset-Fallback Grav plugin which you can install to provide polyfill-based compatibility for every browser.

My plugin leverages the awesome Picturefill polyfill developed and maintained by Filament Group to make Grav's responsive images work on every browser.

The plugin has two modes: passive and active. Passive mode simply includes the Picturefill polyfill on your pages, this polyfill makes sure every browser understands and can use that list of images you provided. So your images will look top notch on every browser.

The polyfill has one drawback though, because it is written in JavaScript, the browsers that do not support srcset by default will already start downloading your default image (the regular, small version) before Picturefill can teach them how to read the srcset. The result being that if your browser decides it needs a larger image, it will have downloaded two images instead of one. This is where active mode comes in. When the plugin is in active mode, it will do some magic in the background to adjust the structue of your HTML to make sure no images are downloaded before Picturefill has thought your browser how to go about using the list of images you provided. And don't worry, I made sure it doesn't change how your website looks, it's valid HTML and the images will still become visible on browsers that have JavaScript turned off.

Well, that's all for today. If you have any questions about this or just feel like a chat, I can be found on the Grav gitter chatroom most of the time.