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 [email protected]:alexwlchan/
$ 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.

Testing in Travis

I run a small number of tests in Travis, which look for particular strings in the rendered HTML.
I'm not trying to test Jekyll itself (that's best left to the Jekyll developers) – more test that I haven't broken something with a config change.
Stuff like footnotes, syntax highlighting, and so on – I have a bad habit of breaking them and not noticing in.

Tests are a good way to document the fiddly details buried in the templates and the like.

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.

  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][drangtweet].

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 Travis, 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.