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

The main 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
—

—

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.

The seasons

The site dresses itself for the four seasons and for the holidays below: the colours change, and each brings its own decorations, cursor and a few sounds (credited below). When a holiday falls inside a season, the holiday gets the day. Preview any of them; a preview lasts until you close the tab.

    Back to today's season · Turn the seasons off

    Photographs and hosting

    Photography
    My own, shot on a Canon EOS 60D (the older photos) and a Canon EOS R (the newer ones)
    Full-size files
    In a GitHub release rather than the repository, because Pages has an artifact limit and hundreds of originals do not fit inside it
    Hosting
    GitHub Pages, on a custom domain

    The whole thing is written up, at considerably more length than this, in How This Site Is Built.

    The slideshow's sounds

    Other people's recordings, shared under free licences, each cut into a loop for the slideshow.

    Fire
    Fireplace Sound loop by PagDev (CC0)
    Rain
    Regen in de tent by luc de bruijn (CC BY 3.0)
    Rain and thunder
    Thunderstorm after hot summer day by stephan (public domain)
    Forest
    A dawn chorus in Shipton Gorge by Silas S. Brown (public domain)
    River
    Forest lawn creek by Dsw4 (public domain)
    Café
    Cafe ambiance by Marble Toast (CC0)
    City at night
    Two Lane Road in Kelowna at Midnight by Extemporalist (CC0)

    The seasons' sounds

    Each season puts three things on the footer, and if you turn the sounds on, most of them play a recording when clicked, also shared under free licences.

    Spring
    Summer
    Fall
    Winter
    Burns Night
    • Pipes from the United States Air Force Reserve Pipe Band (public domain)
    • A toast from Work With Sounds and the Technical Museum of Slovenia (CC BY 4.0)
    • The slideshow's fire
    Darwin Day
    • Summer's waves
    World Book Day
    Holi
    May the Fourth and Revenge of the Fifth
    International Cat Day
    Halloween
    Diwali
    Bonfire Night
    St Andrew's Day
    Hogmanay