22 June 2026

How I built this site

A portfolio that doubles as an open template, built AI-native with Claude Code. The stack, the process, and how to make it your own.

This site is two things at once: my portfolio, and a template anyone can fork to build their own. This post is the short story of how it came together, how to run it, and what to change to make it yours.

The stack

It is built with Astro, which suits a content-driven site like this. The pages render to static HTML at build time and ship almost no JavaScript, so it loads fast and stays cheap to host. The work and these posts are Astro content collections (typed markdown), the styling is Tailwind v4, and the few interactive pieces (the theme toggle, the mobile menu, the contact form) are small scripts rather than a framework.

The look carries my CV brand straight onto the web: deep olive and mustard, generous whitespace, a serif for headings and a clean sans for everything else. There is a light theme and a dark theme, and the whole palette is driven by two colours, so re-theming is a two-line change.

It deploys to Cloudflare Pages as static files, which also means it runs the same on Vercel, Netlify or GitHub Pages.

How it was built

This is also a small demonstration of how I work now. I built it by directing Claude Code from a written brief, the same spec-first way I work on everything else.

I wrote the spec, then had specialist agents draft in parallel: one on the architecture, one on the design system, one on the words, and a writer for each project, each working from my CV and notes. Then a second pass of reviewers checked the result and argued with it: a fact-checker against my CV, a copy editor for voice, a designer reading the actual rendered screenshots, and an architecture review. They caught real things, a colour that failed contrast in dark mode, a few inconsistent margins, and some copy that needed tightening.

The agents draft and check. I make the decisions and own the corrections. That is the part that matters: a loose brief gets you confidently wrong output faster than ever, so being clear about what to build, and checking what comes back, is the work.

Run it locally

You need Node 22 or newer and pnpm.

pnpm install
pnpm dev

Open http://localhost:4321. Build the static site with pnpm build and preview it with pnpm preview.

Make it your own

Everything personal lives in two places, and you should not need to touch a component.

  1. site.config.ts holds your name, title, contact details, social links, navigation, the two brand colours, and all the page copy (home, about, skills, contact).
  2. src/content/projects/ holds one markdown file per project. The filename becomes the URL, and the frontmatter is validated, so a typo fails the build instead of shipping something broken.

Then swap src/assets/portrait.jpeg for your photo and public/cv.pdf for your CV, paste a free Formspree form id into site.config.ts to switch on the contact form, and write a post like this one in src/content/blog/.

To re-brand, change two colours:

brand: {
  primary: "#34492e", // deep olive
  accent: "#d8aa2f",  // mustard gold
}

Deploy

On Cloudflare Pages, set the build command to pnpm build and the output directory to dist. Push to your default branch and it ships. Set your real domain in astro.config.mjs first, so the canonical links and the sitemap are correct.

That is the whole thing. If you fork it, I would like to see what you build.