Performance is one of the key reasons I chose to build my site using a static site generator. But high quality images are also important to me, and I did not want to chose between imagery and performance. Early in the process, I spent some time learning about responsive image methods and later created a liquid include tag based on what I learned.
There are a number of great articles that go into different methods. I’ve listed them at the bottom.
Quick note: Rouge, the syntax highlighter used by default with Jekyll, does not seem to know how to handle the new attributes used with the img
element. Rouge also appears confused by the combination of the new img
attributes and liquid. Some of the embedded snippets below may look a bit strange below as I work out the kinks.
Creating clever solutions can backfire when you have to regularly use them or maintain them. This is one of the reasons I like the simplicity of using srcset with width descriptions over alternate css, picture or javascript solutions.
At its simplest, srcset
and size
attributes tell the browser what to anticipate the layout will do, and image sizes available to use in that layout. With that information, the browser determines which image file to actually display.
I’m a big fan of letting a browser do what it does best: displaying content the best way it can. srcset
let’s the browser keep that power.
Here is basic starting example:
src
and alt
attributes are still necessary for fallback and accessibility support. But aside from this, we are giving the browser two sets of pieces of information: srcset
and sizes
.
srcset
is where the design power lies. You can have as many or as few files listed per image element as you’d like.
It’s a list of file paths followed with a specific width description for that image. Each image included in the list is separated by commas. Width descriptions get a little weird with srcset
, using a vague w
unit. For example, 100w
means 100px wide. Other image sizes might be 1200w
(1200px), 800w
(800px), or 1920w
(1920px), and so on.
This list of images let’s you micromanage the experience your users have. The browser doesn’t care if this is the exact same image scaled down or not. It cares about the file path and the image widths. This allows us to use any art direction technique within the images themselves that we may want.
Or, you can batch run your largest image size through an editor to scale down at pre-defined intervals. (Don’t forget to run the output images through an optimizer such as TinyPNG, or TinyJPG!)
On its own, srcset
gives you carrots but not carrot cake. There isn’t enough information here for the browser to determine which image to use. Your mark up is only informing the browser what sizes are available.
The sizes
attribute helps the browser identify how to use the provided image sizes. In my super basic example:
I only use sizes=“100vw”
. This tells the browser that this image should display at viewport width 100%, all the time.
In reality, your layout will likely change depending on the browser size. (Responsive design, y’all.) The sizes
attribute can handle media queries, pixel units, viewport widths, and calc(). Separate each part of the list with commas.
All parts combined, this looks something like:
Yeah, okay, it’s not actually that simple. But relative to other responsive method solutions out there, this one still feels the most intuitive and straight forward to me.
Here is an example from another blog post how an image element looks in the markup:
This method isn’t without its drawbacks. Aside from making the img
element more complex, you also do not get fine control over which image displays when. This means that you may end up with moments where your images will appear blurry or pixelated to your users. I personally believe that despite this, this method wins out over others.
This is one part of this method that still bums me out a bit. That img element above is a lot to include in a markdown file. Especially, if you have a block of several images as part of your content.
Using an include to handle responsive images is going to be overkill for some folks. It’s really going to be most useful if you are wanting to use the images in a layout template specifically.
Here is an example of how my some of my image block templates look in the wild. (Full blog post here)
The most direct solution is to insert a block of html and img elements as shown in the previous markup image. But then you lose the benefit of Jekyll and liquid — reusable templates.
I will assume that if you are continuing to read, you have some familiarity with Jekyll, liquid, and includes.
The example img element we looked at earlier has three spots that can be quickly changed to liquid:
{{ site.baseurl }}
is an default Jekyll tag that will give you the base URL necessary for your file path.{{ include.img_name }}
creates an argument for your fallback filename.{{ include.img_alt }}
creates an argument for the alt text of your image.There are a couple of built in assumptions into the above snippet:
filepath
It’s easy enough to change both:
filepath
. For example, {{ include.img_path }}
img_name
argument.Here is where I found having a consistent naming structure is key. Looking back at our initial example using srcset:
…we now have several images we need to account for. The simplest solution is to name your images as similarly as possible. For example, use the same file name, but only modify the suffix. For example: “image_x,” “image_y”, “image_z.”
By doing this you can reduce the number of required attributes to one image name, and its associated widths.
In its simplest form, I am declaring the file path. That path looks like this: http://domain.com/filepath/img_name_w1.jpg
. This image has a width description that equals w1
, etc. To make this work, you only need to name your files with one name, and use a suffix matching the image width.
All together: (I am assuming the middle image size is the fallback, your mileage may vary.)
And when you call it outside of the include, it should look something like this:
Alright. Not so bad. But with only one image, and no layout requirements, this setup may not be worth the work.
As you could see in my screenshots above. My images appear as part of layout templates. Including captions, and multi-image blocks. This is not that hard to do. But once again, a consistent naming pattern will simplify this.
That include is definitely getting longer and harder to read. And this is the include tag you would insert into your post or page:
Compared to the mess that comes before it, this solution is far simpler! Even if you were including straight HTML and not a templated version, responsive images get verbose.
And in the particular example I used above — I made the caption optional with an if statement. For this include, the two images and their associated attributes are always required. But the caption is optional.
This isn’t the most elegant solution, but it is the easiest to remember and so far the most effective. You can also make almost any part of the the img element and its associated attributes part of your template. That’s great for adding customization to your individual blocks. You can even use include attributes to add optional css classes. Think about all that design potential!
One of the arguments I had earlier on in favor of the srcset
and sizes
method was that you have granular control over the experience users will have.
When you begin to create a templated include, however, you lose some of that flexibility. In the specific examples above, there must always be 3 image file paths and widths provided as arguments for the include. At the moment. There are times when having 3 versions of one image simply doesn’t make sense.
In that instance, we can make our include templates a bit more verbose, but allow for more flexibility. Even so, we will unintentionally or not set a max number of widths we can support. Here is an example of using {% if %}
statements as part of the include to allow up to five image widths.
The include tag you would use in posts and pages:
You can use this same method in any multi-image block. Just combine the {% if %}
statements with any <img>
element used as part of a template.
Including responsive images in your website is going to be a moderate pain in the rear. But there are ways to make the process a little easier for templates in Jekyll.
An even smoother solution would be a Jekyll plugin. Here are two that I looked at:
Unforunately, neither exactly fits how I’d like to have my images behave. So, for now I’ll continue to use the include method I’ve shown above.