Skip to content
Source code and plugins for my website, a static site built with Jekyll
Branch: live
Clone or download
Latest commit f966c51 Jun 18, 2019
Type Name Latest commit message Commit time
Failed to load latest commit information.
.github Use the workflow defined externally Mar 10, 2019
docs Add a way to exclude posts from the global index Apr 9, 2019
misc Save the source for the Unix time diagrams in the repo May 15, 2019
src Publish new post Jun 18, 2019
.dockerignore Add the generated _site directory to .dockerignore Sep 24, 2018
.gitignore Clean up an unused entry from the .gitignore Mar 10, 2019
Dockerfile Images are prebuilt so optimisations for CI can be ditched Mar 15, 2019
Gemfile Link to the category from the post metadata Mar 19, 2019
Gemfile.lock Link to the category from the post metadata Mar 19, 2019
Makefile Bump the Docker image version Jun 18, 2019 Remove redundant references to Travis in the README Jun 15, 2019
_config.yml Add more quoting to _config.yml [skip ci] Feb 26, 2019
azure-pipelines.yml Drop the env? Jun 1, 2019
create_post.rb Create an empty category on new posts Jun 9, 2019 Verbosity is always helpful! Mar 15, 2019 Add all the images and initial draft for my Forth Bridge post Mar 15, 2019 Recut all the images with ImageMagick Mar 21, 2019

Build Status

This repo has the code for my personal site, It's a static site built with Jekyll, with a number of plugins written to suit my personal tastes.

The site is built and tested by Azure Pipelines. When I push to master, Azure uploads a copy of the rendered HTML files to a Linode server, where they're served by nginx.

Getting started

You need Git, make and Docker installed.

To run a local copy of the site:

$ git clone
$ make serve

The site should be running on http://localhost:5757. If you make changes to the source files, it will automatically update.

To build a one-off set of static HTML files:

$ make build

Technical details

Builds in Docker

I used Jekyll (well, Octopress) for the first iteration of my site, but I kept having issues with Ruby. Half the time, I'd come to write something, and find I was unable to build the site! Clearly sub-optimal.

Drawing inspiration from what we do at Wellcome, I've pushed the entire build process inside Docker. When I want to build the site on a new machine, I don't need to worry about installing dependencies – it's managed entirely by Docker.

Atom feed generation

For Atom feeds, I have my own template and a few custom filters. I don't use jekyll-feed because I sometimes want an entry to link somewhere other than my site (Daring Fireball-style link posts), and that's not supported.

If I want a post to link elsewhere, I add link to the post frontmatter:

title:  A validator for RSS and Atom feeds
layout: post
date:   2017-09-22 08:19:42 +0100

Because I'm rolling my own feeds, I use rubys/feedvalidator to test I'm really producing valid Atom markup. See tests/


I write all my stylesheets in SCSS. The component SCSS files are in _scss, and they're pulled together in _main.scss. The output is a single, minified, CSS file.

The colours and layout variables are defined in _settings.scss. Note that $primary-color is defined as follows:

$primary-color: #d01c11 !default;

The !default marker means this variable is defined only if it isn't already defined – and I use this to produce alt-colour versions of the stylesheet. If I add the following front matter to a post:

  color: 6c006c

then I get a version of the stylesheet that uses #6c006c as its primary colour, and the page loads that stylesheet instead. You can see an example in my docopt slides.

The theme colour is also used in the favicon (which has to be created manually) and in the header image (which is created automatically using specktre).

The heavy lifting is done in _plugins/theming.rb.

Other theming settings

In the same vein as page colour, I can override a couple of other settings in the theme: front matter. Specifically:

  card_type: summary_large_image    # If I want to change the Twitter card type
  image: /images/2017/P5280917_2x.jpg
                                    # If I'm using summary_large_image, a path
                                    # to the image to use
  touch_icon: docopt                # Override the apple-touch-icon setting,
                                    # and the icon used in social sharing links

These settings are used in the template logic. The assets get saved in the theme directory, and have to be created manually.

Month and year archives

The format of my post URLs is:


Because I'm old-fashioned and think URLs are meaningful, it feels to me that /:year/:month/ should show you a collection of all the posts in that month, and /:year/ should do the same for the year. The path can be treated as a directory structure – which it is, if you look at the generated files!

To that end, I'm using another plugin to generate them just the way I like. It's a fork of jekyll-monthly-archive-plugin, but with my own template and support for yearly archives as well.

Twitter embeds

For embedded tweets, rather than using Twitter's embed function (which comes with all sorts of JavaScript and tracking and slowness), I render tweets as static HTML. This is an idea I originally got from Dr. Drang.

To embed a tweet in a post, I use the following tag:

{% tweet %}

When the site is built, I have a personal plugin that:

  • Polls the Twitter API
  • Caches the complete API response and a copy of the author's avatar
  • Uses the cached API response and a template to render an HTML snippet

Polling the Twitter API requires a set of API tokens, but I check in the cached responses (see _tweets). This means that I can fetch the tweet data on a local machine, but when I push to CI, it doesn't need my credentials to render the tweet.

Because I render the tweets at compile time, I can change the appearance of old tweets by updating the template, without having to edit old posts. That's part of why I keep the entire API response – in case I later need data I'd thrown away the first time.


I'm only interested in hearing about bugs or typos – please don't open an issue because you think I'm a terrible writer! 😜

If you want to use any of the components – plugins, layouts, stylesheets – feel free to do so.


The footer of the website says:

Prose is CC-BY licensed, code is MIT.

That's because this is a mixture of prose content (blog posts) and code (both the repo itself, and code I write about). I like CC-BY for the prose content, but Creative Commons licenses aren't suitable for code, so for that I use the same MIT license I do for my other open-source code.

You can’t perform that action at this time.