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
| Check | Tool |
|---|---|
| Types, lint, format | tsc, ESLint, Prettier |
| Accessibility (WCAG 2.1 AA) | axe-core via Playwright |
| Keyboard and theme flows | Playwright |
| Lighthouse budgets | Lighthouse CI, all scores ≥ 0.95 |
If any of these fail, the change does not merge.