Under the hood

How this is built

Hand-written HTML, CSS and JavaScript, served straight off GitHub Pages. No framework, no bundler, no build step between the repository and the page you are reading. Everything below is measured from the repository itself by generate_colophon.py rather than typed in, so it cannot go out of date without a check failing.

What it weighs

Stylesheet

Icon fonts

Text fonts

JavaScript

Dependencies
0

No npm, no lockfile, nothing to audit

Build step on deploy
None

Pages serves the repository as written

One stylesheet is authored; the served file is generated by inlining the vendor CSS and then deleting every rule whose selector matches nothing on the site. The icon fonts are subset to the glyphs actually used, which is where most of that saving comes from. Nothing is minified beyond the stylesheet, because the numbers above did not justify the extra moving part.

What is in it

Pages
Posts
Words
Reading time

End to end, at 220 words a minute

Series
Topics
Photographs

Posts with a live demo

The writing

First post

Posts a month

Averaged across the whole run

Average post

words

Longest

Shortest

Commits

How it was built, month by month

Commits per month since the repository began

Every commit to the repository, by month. The site was a single page for years; the writing, the tooling and most of what the rest of this page describes arrived in one long push through 2026.

What it runs on

It uses

  • Hand-written HTML, CSS and JavaScript
  • GitHub Pages, from the repository's default branch
  • Python for the build scripts and the audit
  • Playwright to drive the interactive posts in a real browser
  • A service worker, so visited pages survive going offline
  • GitHub Discussions for comments, Last.fm and Goodreads for the live figures

It deliberately does not

  • Use a framework, a bundler, or a package manager
  • Ship a single third-party runtime dependency
  • Carry advertising, or any tracker beyond one analytics tag
  • Require JavaScript to read a post
  • Animate anything that a reader has asked it not to
  • Publish a figure on this page that was typed rather than measured

The build

There is no build on deploy, but there is one before a commit. scripts generate what would otherwise be maintained by hand, and every one of them has a --check mode that fails rather than silently repairing.

minify_css.py
Inlines the vendor imports, deletes rules nothing matches, points the icon fonts at their subsets
generate_read_times.py
Counts each post's words, which also decides its band in the blog filter
generate_post_facets.py
Detects which posts carry a live demo or code, for the Format filter
generate_related_posts.py
Ranks neighbours by TF-IDF over body text and bakes the cards in
generate_feed.py
Builds the RSS feed, full-text for the twenty newest posts
subset_icon_fonts.py
Cuts three icon fonts down to the glyphs the site actually uses
audit_site.py
Links, metadata, structured data, accessibility basics, feed and sitemap
generate_colophon.py
Measures everything on this page

How it is checked

Checks per push

All must pass before the site deploys

Audit baseline
0 / 0

Errors and warnings, across every page

Lines of CSS

One authored file

Lines of JavaScript

A push runs the site audit, rebuilds the stylesheet and fails if the committed one differs, and drives every interactive post in a headless browser to check it still converges, still works at phone width, and still announces itself to a screen reader.

Type and colour

Poppins

Interface, navigation, headlines on photographs. Self-hosted, seven weights.

Lora

Section headings and the body of every post. Self-hosted, three weights plus italics.

Gradient wordmark, hero, rules Red anything you can click Stone labels and decoration Surface warm off-white, never pure

One accent does one job. Red means a control; the gradient is reserved for the two places the site signs its own name. Everything decorative is neutral, because 492 photographs are colour enough for one website.

Photography is my own, shot on a Canon EOS 60D; the full-size files live in a GitHub release rather than the repository, because Pages has an artifact limit and 492 originals do not fit inside it. Hosting is GitHub Pages on a custom domain. The whole thing is written up, at considerably more length than this, in How This Site Is Built.