← Home
Colophon · How this is built

Colophon

This site is the only thing in my portfolio you can read the source of. Here is what it is made of, and why it is made that way.

0
Front-end dependencies
0
Build steps
128KB
First visit, fonts and all
62
Design tokens

What it is

Twelve pages of HTML, one stylesheet and one script, written with Claude Code: I set the direction and reviewed every change, the agent wrote the code. No framework, no bundler, no build step: the files your browser receives are the files we wrote. That is roughly 1,980 lines of CSS and 890 of JavaScript for the whole site, and it means there is no layer between a decision and its consequence.

I build this way because I lead teams that ship real software, and I want my own estimate of what a thing costs to stay honest. A framework would make this site easier to write and much harder to understand.

Type and grid

Helvetica Neue for display, at whatever weight the machine has, falling back to Helvetica and then Arial. No webfont for the headlines: the face I want is already on most of the machines that matter, and shipping a substitute for the rest costs more than it returns. Inter at 400 to 600 for running text, and one weight of JetBrains Mono for the uppercase labels, stat captions and index numerals. For a long time those labels were Inter with wide tracking doing an impression of a monospace; they are a real one now, which is the difference between a technical register and the look of one. A twelve-column grid with a 24px gutter on an 8pt scale, capped at 1536px. Sixty-two custom properties hold the type scale, spacing, colour and easing, so a change lands everywhere at once.

The design direction was explored and chosen in Northlit, HangarX's agentic design suite, and then implemented with Claude Code. The system is called Low Anchor.

Motion

Twenty-eight scroll-driven animations, all of them CSS. No scroll library, no intersection observers for the reveals: animation-timeline: view() and scroll() do the work, inside a feature query, so a browser without them simply shows the finished state. The light theme turns dark as you scroll and back as you return; the toggle pins either one.

Everything is gated behind prefers-reduced-motion. If you have reduce-motion on, the site does not animate at all, and nothing is hidden waiting for an animation that will never run.

The assistant

The chat is a serverless function on the Anthropic Messages API, streaming over server-sent events so the answer arrives as it is written. It answers only from a knowledge base kept in the repository next to the code, which is sent as a cached prompt prefix. It is instructed to say when it does not know something rather than guess, and it does not speak to whether I am open to roles.

Two packages, @anthropic-ai/sdk and @vercel/kv, are the only dependencies in the project, and they run in that function. Nothing ships to your browser.

For AI agents

This site is agent-ready. In a browser whose AI assistant supports WebMCP, the pages offer that assistant a set of tools, so it can ask the portfolio structured questions instead of reading the page like a screenshot. Every answer comes from the same words the pages show, and carries the link it came from.

To use it, open any page here with an assistant that supports WebMCP and ask in plain language. Some things to try:

  • “Summarise Isaac’s experience designing agentic AI, with sources.”
  • “What calls did he make on Northlit, and why?”
  • “Show me the evidence for Digital Pool.”
  • “Fit check against this job description:” followed by the role.
  • “How do I get in touch with him?”

The assistant can list the case studies, read any of them or a single section, search all of the work and the archive, read the résumé, ask this site’s own assistant, get the contact links, and scroll your page to the part it is citing. Every tool is read-only: nothing books, sends or submits anything on your behalf, and the assistant still doesn’t speak to whether I’m open to roles. The data behind it is one file, rebuilt from the pages whenever they change. Browsers without WebMCP download none of it.

What it does not do

Google Analytics 4 counts page views and a handful of chat events, so I know which pages get read and what people ask. It runs without cookies unless you allow one, which only makes a return visit count as the same person. No ad pixels, no cross-site tracking, and the assistant logs questions without any identity attached. Change your choice.

One external request: two typefaces from Google Fonts. Everything else is served from this domain.

Those typefaces are, by a distance, the heaviest thing here: about 68KB against roughly 60KB for every line of markup, style and script on the site compressed. They are served as variable fonts, which is half what the same families cost as separate weights, and the browser keeps them after the first page. I would rather say that plainly than quote the flattering number.

Hosting and addresses

Static files on Vercel, with the one function for the chat. URLs have no extension and every page declares a canonical, so there is exactly one address for each thing. Every page carries Open Graph tags, structured data and an entry in the sitemap; there is a plain-text summary of the whole site for assistants that would rather read than crawl.

Credits

The Alaska photographs in the My Story gallery and on the My Story page are Creative Commons or public domain, credited individually in the gallery and in CREDITS.md. The Google product screenshots on the Google case study come from Google Cloud's public documentation and are Google's, not mine.

If you want to see how any of it works, the source is right there: v3.css and v3.js.