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
—

Run on every push and pull request

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 hundreds of 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 hundreds of 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.