├── .gitignore ├── Gemfile ├── Gemfile.lock ├── Rakefile ├── _config.yml ├── _examples ├── 1-base.md ├── 2-srcset.md └── 3-imgix.md ├── _includes ├── image.html ├── srcset-with-imgix.html └── srcset.html ├── _layouts └── default.html ├── favicon.png ├── images ├── 500 │ ├── example-1.jpg │ └── example-2.jpg ├── 750 │ ├── example-1.jpg │ └── example-2.jpg ├── 1000 │ ├── example-1.jpg │ └── example-2.jpg ├── example-1.jpg ├── example-2.jpg └── source │ ├── example-1.jpg │ └── example-2.jpg ├── index.md ├── notes.md ├── readme.md └── s3_website.yml /.gitignore: -------------------------------------------------------------------------------- 1 | _site 2 | .DS_Store 3 | .bundle/ 4 | .sass-cache 5 | .jekyll-metadata 6 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'jekyll', '~> 3.1' 4 | 5 | group :jekyll_plugins do 6 | gem 'jekyll-imgix' 7 | end 8 | 9 | 10 | # the following gems are not needed for jekyll or imgix directly 11 | gem 'rake' # only used for convenient serve/deploy commands 12 | gem 's3_website' # only used for deploying 13 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | addressable (2.4.0) 5 | colorator (0.1) 6 | colored (1.2) 7 | configure-s3-website (1.7.1) 8 | deep_merge (= 1.0.0) 9 | deep_merge (1.0.0) 10 | dotenv (1.0.2) 11 | ffi (1.9.10) 12 | imgix (1.1.0) 13 | addressable 14 | jekyll (3.1.3) 15 | colorator (~> 0.1) 16 | jekyll-sass-converter (~> 1.0) 17 | jekyll-watch (~> 1.1) 18 | kramdown (~> 1.3) 19 | liquid (~> 3.0) 20 | mercenary (~> 0.3.3) 21 | rouge (~> 1.7) 22 | safe_yaml (~> 1.0) 23 | jekyll-imgix (1.1.0) 24 | imgix (~> 1.1.0) 25 | jekyll-sass-converter (1.4.0) 26 | sass (~> 3.4) 27 | jekyll-watch (1.4.0) 28 | listen (~> 3.0, < 3.1) 29 | kramdown (1.11.1) 30 | liquid (3.0.6) 31 | listen (3.0.7) 32 | rb-fsevent (>= 0.9.3) 33 | rb-inotify (>= 0.9.7) 34 | mercenary (0.3.6) 35 | rake (11.1.2) 36 | rb-fsevent (0.9.7) 37 | rb-inotify (0.9.7) 38 | ffi (>= 0.5.0) 39 | rouge (1.10.1) 40 | s3_website (2.13.0) 41 | colored (= 1.2) 42 | configure-s3-website (= 1.7.1) 43 | dotenv (~> 1.0) 44 | thor (~> 0.18) 45 | safe_yaml (1.0.4) 46 | sass (3.4.22) 47 | thor (0.19.1) 48 | 49 | PLATFORMS 50 | ruby 51 | 52 | DEPENDENCIES 53 | jekyll (~> 3.1) 54 | jekyll-imgix 55 | rake 56 | s3_website 57 | 58 | BUNDLED WITH 59 | 1.11.2 60 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | require 's3_website' 3 | 4 | desc "custom Jekyll serve for local development (with imgix)" 5 | task :serve do 6 | system "JEKYLL_ENV=production bundle exec jekyll serve" 7 | end 8 | 9 | # don’t try running this – it won’t work for you! 10 | desc "build and deploy scripts, images and site to production servers via s3_website" 11 | task :deploy do 12 | system "JEKYLL_ENV=production bundle exec jekyll build" 13 | system "s3_website push" 14 | puts "## Deployed site to S3 ##" 15 | end 16 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | image_url: /images 2 | 3 | collections: 4 | examples: 5 | output: true 6 | permalink: /:path/ 7 | 8 | defaults: 9 | - 10 | scope: 11 | type: examples 12 | values: 13 | layout: default 14 | 15 | sizes: '(min-width: 70em) 1000px, (min-width: 50em) 750px, (min-width: 31.5em) 500px, 100vw' 16 | 17 | srcset: 18 | - 500 19 | - 750 20 | - 1000 21 | 22 | ## Gems configuration 23 | gems: 24 | - jekyll/imgix 25 | 26 | ## Imgix configuration 27 | imgix: 28 | source: demo-jekyll-imgix.imgix.net 29 | -------------------------------------------------------------------------------- /_examples/1-base.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Example: no `srcset`' 3 | image: 4 | - src: example-1.jpg 5 | alt: 'Leaves and a blue sky.' 6 | - src: example-2.jpg 7 | alt: 'Fall leaves on the ground at sunset, with a dog walking in the background.' 8 | --- 9 | 10 | {% assign image = page.image[0] %} 11 | {% include image.html %} 12 | 13 | {% assign image = page.image[1] %} 14 | {% include image.html %} 15 | -------------------------------------------------------------------------------- /_examples/2-srcset.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Example: `srcset`' 3 | image: 4 | - src: example-1.jpg 5 | alt: 'Leaves and a blue sky.' 6 | - src: example-2.jpg 7 | alt: 'Fall leaves on the ground at sunset, with a dog walking in the background.' 8 | --- 9 | 10 | {% assign image = page.image[0] %} 11 | {% include srcset.html %} 12 | 13 | {% assign image = page.image[1] %} 14 | {% include srcset.html %} 15 | -------------------------------------------------------------------------------- /_examples/3-imgix.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Example: `srcset` plus imgix' 3 | image: 4 | - src: /images/example-1.jpg 5 | alt: 'Leaves and a blue sky.' 6 | quality: 40 # change this value to a higher or lower value to change the quality of the image 7 | - src: /images/example-2.jpg 8 | alt: 'Fall leaves on the ground at sunset, with a dog walking in the background.' 9 | quality: 60 10 | --- 11 | 12 | {% assign image = page.image[0] %} 13 | {% include srcset-with-imgix.html %} 14 | 15 | {% assign image = page.image[1] %} 16 | {% include srcset-with-imgix.html %} 17 | 18 | These images are hosted on an S3 bucket which is connected to an imgix source. If you are using this demo you will not be able to update the S3 or imgix sources but you can test the imgix plugin parameters for the two demo images. 19 | {:.note} 20 | -------------------------------------------------------------------------------- /_includes/image.html: -------------------------------------------------------------------------------- 1 | 2 | {{ image.alt }} 5 | -------------------------------------------------------------------------------- /_includes/srcset-with-imgix.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% if image.quality %} 4 | {% assign quality = image.quality %} 5 | {% else %} 6 | {% assign quality = 50 %} 7 | {% endif %} 8 | 9 | {{ image.alt }} 14 | -------------------------------------------------------------------------------- /_includes/srcset.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ image.alt }} 8 | -------------------------------------------------------------------------------- /_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ page.title }} 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 108 | 109 | 110 | 111 | 112 |

{{ page.title | markdownify }}

113 | 114 | {{ content }} 115 | 116 | {% if page.permalink != '/' %} 117 |

back to index

118 | {% else %} 119 |

view source

120 |

view video

121 | {% endif %} 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opattison/demo-jekyll-imgix/f75b5fd21d89825f34a014d3e28b609524c449e6/favicon.png -------------------------------------------------------------------------------- /images/1000/example-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opattison/demo-jekyll-imgix/f75b5fd21d89825f34a014d3e28b609524c449e6/images/1000/example-1.jpg -------------------------------------------------------------------------------- /images/1000/example-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opattison/demo-jekyll-imgix/f75b5fd21d89825f34a014d3e28b609524c449e6/images/1000/example-2.jpg -------------------------------------------------------------------------------- /images/500/example-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opattison/demo-jekyll-imgix/f75b5fd21d89825f34a014d3e28b609524c449e6/images/500/example-1.jpg -------------------------------------------------------------------------------- /images/500/example-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opattison/demo-jekyll-imgix/f75b5fd21d89825f34a014d3e28b609524c449e6/images/500/example-2.jpg -------------------------------------------------------------------------------- /images/750/example-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opattison/demo-jekyll-imgix/f75b5fd21d89825f34a014d3e28b609524c449e6/images/750/example-1.jpg -------------------------------------------------------------------------------- /images/750/example-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opattison/demo-jekyll-imgix/f75b5fd21d89825f34a014d3e28b609524c449e6/images/750/example-2.jpg -------------------------------------------------------------------------------- /images/example-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opattison/demo-jekyll-imgix/f75b5fd21d89825f34a014d3e28b609524c449e6/images/example-1.jpg -------------------------------------------------------------------------------- /images/example-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opattison/demo-jekyll-imgix/f75b5fd21d89825f34a014d3e28b609524c449e6/images/example-2.jpg -------------------------------------------------------------------------------- /images/source/example-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opattison/demo-jekyll-imgix/f75b5fd21d89825f34a014d3e28b609524c449e6/images/source/example-1.jpg -------------------------------------------------------------------------------- /images/source/example-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opattison/demo-jekyll-imgix/f75b5fd21d89825f34a014d3e28b609524c449e6/images/source/example-2.jpg -------------------------------------------------------------------------------- /index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Demo: Responsive images with Jekyll and imgix' 3 | layout: default 4 | permalink: / 5 | --- 6 | 7 |
8 | 9 |
    10 | {% assign examples = site.examples %} 11 | {% for example in examples %} 12 |
  1. {{ example.title | markdownify | remove: '

    ' | remove: '

    ' }}
  2. 13 | {% endfor %} 14 |
15 | 16 |
17 | -------------------------------------------------------------------------------- /notes.md: -------------------------------------------------------------------------------- 1 | # Speaking notes 2 | 3 | Hi! I’m Oliver Pattison, an independent designer and developer. This is a quick introduction to implementing responsive images for Jekyll using `srcset`. I will demonstrate how to generate responsive images with Liquid includes, and imgix, an image processing service. Many of us have chosen static sites for their speed, but with some adjustments to our process, we can make them even faster. 4 | 5 | ## Stats 6 | 7 | The heaviest elements on many websites are images. According to the [HTTP Archive][1] **63.5%** of the average page’s weight was made up of images. 8 | 9 | *(stats retrieved April 1, 2016, based on the top one million websites)*. 10 | 11 | ## Uncertainty 12 | 13 | There are two key factors of uncertainty when building responsively: the size of a device, and the speed of a network connection. In the spirit of Postel’s [robustness principle][2]: let’s be conservative in what we send. 14 | 15 | ## Viewport 16 | 17 | For example: if a client has a 360 pixel-wide viewport, there usually won’t be a benefit in being served a 1000 pixel-wide image. The costs of serving heavier images to people include: 18 | 19 | - slower performance (measured by transfer speeds and memory use) 20 | - and increased bandwidth costs (which is an actual dollar cost for many people – consider metered mobile data plans) 21 | 22 | ## The solution 23 | 24 | Cutting down on the size of served images is one of the most effective methods for improving the perceived performance of an existing site. 25 | 26 | We now have a well-supported web standard to help us deal with image weight by serving the right images at the right sizes: `srcset`. 27 | 28 | ## Block 29 | 30 | First, let’s take a look at some reusable code: a building block that we will use throughout this demo. 31 | 32 | We assign a variable to the image map/sequence (from the post’s YAML front matter) and then use that variable immediately in a block. 33 | 34 | ## Why include 35 | 36 | Why use an include for images? Flexibility. If we have one or more images specified, the same Liquid include can be used for each of them. If our include gets more complicated (as it certainly will when we add `srcset`), we can add complexity without changing the original content or page metadata. 37 | 38 | ## Responsive images (without imgix) 39 | 40 | ### Attributes 41 | 42 | `srcset` and `sizes` are attributes of the `` element that combine to give a browser a hint about what it needs to download. `srcset` provides an array of image sources. The `sizes` attribute resembles CSS media queries giving information about image dimensions at various responsive breakpoints *without* having to download an image first. The challenge here is coming up with the right sources and matching sizes. 43 | 44 | ### Sources 45 | 46 | Remember: the browser chooses its source based on the information we provide in these two attributes. The browser often doesn’t know the size of the image in the page. 47 | 48 | For the simplicity of this demo, we will use three image source sizes. People have asked me: how many sources do you need? It depends on the case. Each design will have its own requirements for `srcset` and `sizes`. 49 | 50 | ### Verbosity 51 | 52 | This syntax is **verbose**. We can’t help it. We need to give the browser options for sources. Each image has multiple URL paths, providing the browser a hint about the image’s relative size on the page with the `sizes` attribute. 53 | 54 | ### srcset parts 55 | 56 | Let’s use a Liquid loop to generate an image block that matches this simple layout’s responsive behavior. 57 | 58 | ### sizes 59 | 60 | Now let’s define a `sizes` string which will be included (site-wide) in every image block that needs a `sizes` attribute. 61 | 62 | ### srcset 63 | 64 | Let’s also define a default `srcset` sequence. These values will be looped to generate every `srcset` attribute in our demo. 65 | 66 | ### Image editing 67 | 68 | The problem is generating those images: we’ll probably need to use image editing software to generate all of the variants, and then give unique names to each file. For a handful of images this is manageable, but what if our site has lots of images and what if we need *many* `srcset` variants of each image? 69 | 70 | ## Responsive images with the imgix plugin 71 | 72 | ### why use imgix 73 | 74 | I saw this problem for sites that feature many images. Each image needs multiple sources, meaning potentially hundreds of sources might need to be generated for a single site. Even with automated tools, that is a lot of work and a lot of room for error. It is also work that might need to be re-done if a design is adjusted or specifications change. 75 | 76 | ### solving the problem 77 | 78 | imgix is part of the solution for my projects. What if we could have only one source per image, and from that we could create many variants at different sizes and flexible quality settings? Using URL parameters, or an imgix plugin that generates custom URLs, we can take a single source and then create effectively unlimited variants. 79 | 80 | We can also build this into any Jekyll site. 81 | 82 | ### extend the example 83 | 84 | Let’s extend our earlier example which had three source variants. 85 | 86 | With some adjustments to the `srcset` Liquid include, the image is now processed by imgix. All variants are generated on request by imgix, and we can even change the parameters if our specifications change. 87 | 88 | ### saving bytes 89 | 90 | In our quest for saving bytes, we made a mistake with our image processing with this JPEG: the quality setting created noticeable compression artifacts in the sky. With a parameter change in the source (specified in the front matter), we can improve the output. 91 | 92 | ### adjusting quality 93 | 94 | Let’s bump the quality from 40 to 70 and then refresh the example page. 95 | 96 | 97 | 98 | 99 | ## Installing imgix 100 | 101 | To see how to configure the imgix plugin for Jekyll, take a look at the documentation for the plugin or review the source for this demo. 102 | 103 | Let’s briefly walk through how to install the imgix plugin for a Jekyll site. This assumes that you already have a working Jekyll site, an imgix account and source, and some images that need hosting. 104 | 105 | ### Gemfile 106 | 107 | In your `Gemfile` add the line: 108 | 109 | ``` 110 | gem 'jekyll-imgix' 111 | ``` 112 | 113 | and then run `$ bundle update`. 114 | 115 | ### Jekyll configuration 116 | 117 | In your `_config.yml` file, add: 118 | 119 | ``` 120 | gems: 121 | - jekyll/imgix 122 | 123 | imgix: 124 | source: YOUR-IMGIX-SOURCE.imgix.net 125 | ``` 126 | 127 | where `YOUR-IMGIX-SOURCE` is the name of your source. 128 | 129 | 130 | ## About imgix 131 | 132 | Here are a few important things to know about imgix: 133 | 134 | ### s3 135 | 136 | I recommend Amazon S3 for hosting with imgix, but any web source will do. Make sure you can deploy your site or load your source images before turning on imgix. 137 | 138 | ### env 139 | 140 | The jekyll-imgix plugin does not do anything unless `JEKYLL_ENV` (the environmental variable) is set to `production`. If the imgix plugin is turned off, a site will *not* fail to build, but images won’t be processed. 141 | 142 | ### cost 143 | 144 | imgix costs money: in my opinion it is well worth it if you’re processing a lot of images and need a CDN to host them. You don’t *need* imgix to process responsive images, but it’s one effective way to do it, and the service has other features worth looking into as well. 145 | 146 | --- 147 | 148 | You can learn more by checking out the repo for this demo on GitHub or reading my recommended resources. 149 | 150 | I am [@olivermakes] / on Twitter and the web – I’m always willing to talk about static sites and excited to see what you’re working on. 151 | 152 | --- 153 | 154 | ## Not covering 155 | 156 | Things I didn’t cover here but you might want to read up on: 157 | 158 | - `` responsive images (useful for art direction but not as essential for improving performance). You can use `` and `srcset` together – they are part of the same specification. 159 | - Using [picturefill][3] to polyfill support for responsive images for wider browser support. 160 | - [Cost of imgix][4]: it’s affordable for what it is capable of (but not free after the first month of use). You *can* use one user account for multiple sites. 161 | - Setting up imgix account and sources (covered fully in the [imgix documentation][5]). 162 | - Other capabilities of imgix for art direction. 163 | 164 | 165 | [1]: http://httparchive.org/interesting.php 166 | [2]: https://en.wikipedia.org/wiki/Robustness_principle 167 | [3]: https://github.com/scottjehl/picturefill 168 | [4]: http://imgix.com/pricing 169 | [5]: https://docs.imgix.com/ 170 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Responsive `srcset` images with imgix 2 | 3 | ## [Demo site](http://demo-jekyll-imgix.s3-website-us-east-1.amazonaws.com/) 4 | 5 | This is a demo Jekyll site made for a [JekyllConf 2016](http://jekyllconf.com/) lightning talk, presented 2016-05-07. 6 | 7 | ## [Video of presentation](https://www.youtube.com/watch?v=BIf6oNpGl74) 8 | 9 | [View on YouTube](https://www.youtube.com/watch?v=BIf6oNpGl74). Speaking notes are in this repo. 10 | 11 | ## [Further notes](https://olivermak.es/2016/05/jekyllconf-responsive-images/) 12 | 13 | I wrote about my responsive images process to accompany this repo [on my website](https://olivermak.es/2016/05/jekyllconf-responsive-images/). 14 | 15 | ## Useful links 16 | 17 | - A foundational piece by Eric Portis on [implementing responsive images with `srcset` and `sizes`](https://ericportis.com/posts/2014/srcset-sizes/) 18 | - [Responsive Images 101 series](http://blog.cloudfour.com/responsive-images-101-definitions/) by Jason Grigsby (Cloud Four) 19 | - [imgix, the real-time image processing service](http://imgix.com/) 20 | - [jekyll-imgix plugin](https://github.com/imgix/jekyll-imgix) 21 | - [My own Jekyll site](https://github.com/opattison/olivermakes/) (which uses responsive images made faster with imgix) – [check out my photography posts](/photography) for examples of `srcset` and imgix in use 22 | 23 | --- 24 | 25 | ## View the demo 26 | 27 | [Demo site](http://demo-jekyll-imgix.s3-website-us-east-1.amazonaws.com/) 28 | 29 | If you do want to run this demo locally to see how Jekyll and imgix work, I have included a `rake` command for running Jekyll with the jekyll-imgix plugin. 30 | 31 | 1. Install Ruby 32 | 2. run `$ bundle install` 33 | 3. run `$ rake serve` 34 | 35 | Edit the `_examples` files or play around with the `srcset` values in the `_config.yml` file. 36 | 37 | Any questions about this demo? Submit an issue or get in touch [on Twitter: @olivermakes](https://twitter.com/olivermakes). 38 | -------------------------------------------------------------------------------- /s3_website.yml: -------------------------------------------------------------------------------- 1 | s3_id: <%= ENV['S3_ID'] %> 2 | s3_secret: <%= ENV['S3_SECRET'] %> 3 | s3_bucket: demo-jekyll-imgix 4 | --------------------------------------------------------------------------------