HTML5 picture element in Jekyll

What a nice view, eh?

This once was a 8.5Meg 4K image. Now how do you see it? If you click “view image” what do you read in the path? On my 1920x1080 desktop monitor I see assets/resized/images/1920/new-york-4k-view-at-sunset.jpg. Cool! It’s a half an hour work, thanks to Jekyll and jekyll-responsive-image plugin by Wildlyinaccurate!

All you have to do is:

  1. add one line (and its comment) to the Gemfile
     # Auto-resize and serve smaller images: https://github.com/wildlyinaccurate/jekyll-responsive-image
     gem "jekyll-responsive-image"
    
  2. run bundle install
     bundle install
    
  3. if something horrible goes wrong (like it happened to me1), install a couple more packets:
     sudo apt install build-essential imagemagick libmagickcore-dev libmagickwand-dev
     bundle update && bundle install
     jekyll clean
    
  4. add jekyll-responsive-image to the used gems into your _config.yml

     gems:
       - jekyll-feed
       - jekyll-paginate
       - jekyll-responsive-image
    
  5. copy almost as-is the configuration found on jekyll-responsive-image plugin page and paste it inside _config.yml2
     responsive_image:
       # [Required]
       # Path to the image template.
       template: _includes/responsive-image.html
        
       # [Optional, Default: 85]
       # Quality to use when resizing images.
       default_quality: 90
        
       # [Optional, Default: []]
       # An array of resize configuration objects. Each object must contain at least
       # a `width` value.
       sizes:
         - width: 1920
           quality: 90
         - width: 1400
           quality: 90
         - width: 1200
           quality: 90
         - width: 1080
           quality: 90
         - width: 800 
           quality: 95
         - width: 480
           quality: 95
         - width: 320  # [Required] How wide the resized image will be.
           quality: 95 # [Optional] Overrides default_quality for this size.
        
       # [Optional, Default: false]
       # Rotate resized images depending on their EXIF rotation attribute. Useful for
       # working with JPGs directly from digital cameras and smartphones
       auto_rotate: false
        
       # [Optional, Default: false]
       # Strip EXIF and other JPEG profiles. Helps to minimize JPEG size and win friends
       # at Google PageSpeed.
       strip: false
        
       # [Optional, Default: assets]
       # The base directory where assets are stored. This is used to determine the
       # `dirname` value in `output_path_format` below.
       base_path: assets
        
       # [Optional, Default: assets/resized/%{filename}-%{width}x%{height}.%{extension}]
       # The template used when generating filenames for resized images. Must be a
       # relative path.
       #
       # Parameters available are:
       #   %{dirname}     Directory of the file relative to `base_path` (assets/sub/dir/some-file.jpg => sub/dir)
       #   %{basename}    Basename of the file (assets/some-file.jpg => some-file.jpg)
       #   %{filename}    Basename without the extension (assets/some-file.jpg => some-file)
       #   %{extension}   Extension of the file (assets/some-file.jpg => jpg)
       #   %{width}       Width of the resized image
       #   %{height}      Height of the resized image
       #
       output_path_format: assets/resized/%{dirname}/%{width}/%{basename}
        
       # [Optional, Default: true]
       # Whether or not to save the generated assets into the source folder.
       save_to_source: false
        
       # [Optional, Default: false]
       # Cache the result of { % responsive_image % } and { % responsive_image_block % }
       # tags. See the "Caching" section of the README for more information.
       cache: true
        
       # [Optional, Default: []]
       # By default, only images referenced by the responsive_image and responsive_image_block
       # tags are resized. Here you can set a list of paths or path globs to resize other
       # images. This is useful for resizing images which will be referenced from stylesheets.
       #extra_images:
       #  - assets/foo/bar.png
       #  - assets/bgs/*.png
       #  - assets/avatars/*.{jpeg,jpg}
    
  6. add a HTML5 template file to be included and populated through Jekyll. In my case, I used the default name _includes/responsive-image.html.
     <picture>
         {% for i in resized %}
             <source media="(min-width: {{ i.width }}px)" srcset="{{ i.path }}">
         {% endfor %}
         <img src="{{ path }}">
     </picture>
    
  7. that’s it, restart Jekyll and try it out replacing your
     ![test-image]({{ "assets/images/my-huge-4k-image.jpg" }})
    

    with

     {% responsive_image path: assets/images/my-huge-4k-image.jpg alt: "ALT-TEXT-My huge undownloadable image" title: "My huge undownloadable image" %}
    

Late evening UPDATE: after a whole day of fiddling with Liquid syntax, I realized that I don’t want, for almost any reason, a 320px image served on a Google Nexus 5 screen (1080x1920px). But this is exactly what was happening on my mobile! Also the Firefox Inspector (Ctrl-I) was totally sure that the right size for a Nexus 5 is 320x480px! Obviously this has something to do with screen size and pixel density, but the problem is that I don’t care. I want full HD pictures on my phone, exactly as on my desktop. This is the output of a whole day working on responsive images, maybe I should think a bit more about it and change my mind :)

And this is the final aspect of _includes/responsive-image.html. It always serves 1920px images :)

<picture>
    <!-- I still don't understand how this stuff works, the only thing I'm sure of is that I don't want to serve a 320px image on a Nexus 5, WTF! -->
    {% assign sorted_sizes = resized | sort: 'width' | reverse %}
    {% for i in resized %}
        {% assign threexres = forloop.index | minus: 1 %}
        {% assign two_x_res = forloop.index %}
        {% assign onepointfive_x_res = forloop.index | plus: 1 %}
        {% assign one_x_res = forloop.index | plus: 2 %}
        {% assign size_minus_one = sorted_sizes.size | minus: 1 %}
        <source media="(min-width: {{ i.width }}px)" srcset="{{ i.path }}, {{ sorted_sizes[0].path }} {{ sorted_sizes[0].width }}w, {{ sorted_sizes[onepointfive_x_res].path }} {{ sorted_sizes[onepointfive_x_res].width }}w, {{ sorted_sizes[two_x_res].path }} {{ sorted_sizes[two_x_res].width }}w, {{ sorted_sizes[threexres].path }} {{ sorted_sizes[threexres].width }}w" sizes="(min-width: 40em) 576px, (min-width: 64em) 720px, (min-width: 72em) 960px, 100vw">
    {% endfor %}
    <img src="{{ path }}">
</picture>


  1. No package 'MagickCore' found or checking for outdated ImageMagick version (<= 6.4.9)... *** extconf.rb failed *** or jekyll 3.3.1 | Error: no implicit conversion of nil into Hash 

  2. I just added a couple of resolutions and enabled caching.