Henry JonathanAI Automation Engineer

How this site is built.

A small site on purpose: a few pages on the surface, a search menu for everything else. Each choice below was made for a reason, and this page keeps the reasons. Most of what follows is measured or read live, in your browser.

This visit

Measuring your visit…

JavaScript
…
Page transitions
…
Motion
…
Theme
…

Structure

Every page is assembled from the same few components. Turn on the outlines to see where each one starts and ends on this page, and the column everything sits in.

Stack

Framework
Astro, every page rendered at build time
7.3
Runtime
Bun, for installs, builds and scripts
1.3
Styling
Tailwind CSS on nine colour tokens
4.3
Type
Geist and Geist Mono, served from this domain
Code
Shiki, highlighted at build time
Hosting
Vercel, deployed on every merge to main

Decisions

In-app browsers such as Instagram's sometimes serve a page from their own cache and lose the separate stylesheet on the way. Inlining it costs about 6 KB per page after compression and removes that failure entirely.

astro.config.ts
export default defineConfig({
  build: { inlineStylesheets: 'always' },
});

Every script, font and image that Astro builds is named after its own contents, so browsers can keep it for a year without asking again; a change gets a new name. Only the page itself is checked on each visit. Helpers shared by several scripts are bundled into one file instead of many small ones.

vercel.json
{
  "source": "/_astro/(.*)",
  "headers": [{
    "key": "Cache-Control",
    "value": "public, max-age=31536000, immutable"
  }]
}

Cross-document View Transitions are a single CSS rule. The header keeps its place, article titles travel from the list into the heading, and browsers without support simply load the next page.

src/styles/global.css
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }
}
[data-vt] { view-transition-name: var(--vt); }

The command menu is rendered at build time from the same data as the pages, so a new article or page appears in it without anyone touching the menu. It follows the combobox pattern, so it works with a keyboard and a screen reader.

src/scripts/command-menu.ts
if (e.key.toLowerCase() === 'k' && (e.metaKey || e.ctrlKey)) {
  e.preventDefault();
  dialog.open ? close() : open();
}

After each production deploy, a GitHub Action works out which pages changed and sends only those to IndexNow. Google, which does not take part, reads a sitemap whose dates come from each article.

.github/workflows/indexnow.yml
on:
  deployment_status:
jobs:
  submit:
    if: github.event.deployment_status.state == 'success'

Satori turns a small layout into SVG with the text already outlined, and sharp turns that into a PNG. No design tool, and no image to forget when a title changes.

src/lib/og.ts
const svg = await satori(tree, { ...OG_SIZE, fonts });
return sharp(Buffer.from(svg)).png().toBuffer();

If your system asks for less motion, the theme switches instantly, pages swap without fading, and nothing slides. The reading line on articles is pure CSS, driven by scroll position rather than JavaScript.

src/styles/global.css
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*) { animation: none !important; }
}

Colour

Nine tokens, swapped as a set when the theme changes. Contrast is measured against the background, live. Click a row to copy its value.

Type

Built from commit aba7426.

Pages
Home
Projects
Background
Writing
About
Now
Uses
ColophonCurrent page
Writing
The Pin Factory in the Back Office
Links
GitHub↗
LinkedIn↗
Threads↗
Instagram↗
X↗
Download CV↓
Actions
Toggle theme
Copy link to this page