This repo has the code for my personal site, alexwlchan.net. 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, Travis uploads a copy of the rendered HTML files to a Linode VPS, where they're served by nginx.
You need Git, make and Docker installed.
To run a local copy of the site:
$ git clone [email protected]:alexwlchan/alexwlchan.net.git
$ 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
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.
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.
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
link: https://github.com/rubys/feedvalidator
Because I'm rolling my own feeds, I use rubys/feedvalidator to test I'm really producing valid Atom markup.
See tests/test_atom_feed.py
.
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:
theme:
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
.
In the same vein as page colour, I can override a couple of other settings in the theme:
front matter.
Specifically:
theme:
card_type: summary_large_image # If I want to change the Twitter card type
# https://dev.twitter.com/cards/overview
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.
The format of my post URLs is:
/:year/:month/:title/
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.
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 https://twitter.com/iamkimiam/status/848188958567800832 %}
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.
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.