Skip to content
~/andrew.moore

Note

How this site is built

A static, prerendered TanStack Start site with a strict CSP, build-time OG images, and one small WebGL scene. The same rules I use for infrastructure, applied to a web page.

1 min read#tanstack #performance #csp #cloudflare

This site is a small thing, but I built it with the rules I use for production infrastructure: every artefact is produced by the pipeline, nothing is configured by hand, and the defaults are secure.

Static by default

The site uses TanStack Start with prerendering on. At build time, Start runs the app on the server, crawls every internal link, and writes plain HTML for each route. Cloudflare Pages then serves those files from the edge. There is no origin server and nothing to patch.

tanstackStart({
  prerender: { enabled: true, crawlLinks: true, failOnError: true },
  sitemap: { enabled: true, host: "https://andrewmoore.io" },
});

failOnError matters. A broken link or a route that throws fails the build, the same way a failed tofu plan stops a pipeline.

A Content-Security-Policy with no unsafe-inline

Hydration needs inline scripts, and those differ per page. Instead of weakening the policy, a post-build step hashes every inline script in every HTML file and writes a per-route Content-Security-Policy into Cloudflare's _headers file:

bun run build   # vite build + prerender + OG images + CSP hashes

Syntax highlighting is done by Shiki at build time. A small rehype plugin turns its inline style attributes into classes, so style-src 'self' holds too.

Performance budget

  • Every page's Largest Contentful Paint element is plain prerendered text, so it paints before any JavaScript runs.
  • The WebGL scene loads after the browser is idle, pauses when it is off screen, and does not animate at all with prefers-reduced-motion.
  • The command palette and terminal load their code on first use.
  • One self-hosted variable font, latin subset only.

Checked in CI

CheckTool
Types, lint, formattsc, ESLint, Prettier
Accessibility (WCAG 2.1 AA)axe-core via Playwright
Keyboard and theme flowsPlaywright
Lighthouse budgetsLighthouse CI, all scores ≥ 0.95

If any of these fail, the change does not merge.