ADR-001: Static-first on Astro, deployed to Cloudflare Pages

Context

This site is an engineering ledger: decision records, system projects, garden notes, and short tips, published daily via git push. Requirements, in priority order:

  1. Publishing friction ≈ zero. Writing happens in Markdown in a local editor; a push must be the entire deploy pipeline.
  2. Zero infrastructure to babysit. No database, no server, no CMS.
  3. Fast and honest. Static HTML, no client-side JavaScript unless a page earns it.
  4. Structured content. Frontmatter must be schema-validated at build time so the taxonomy (ADR statuses, garden stages) cannot silently rot.

Decision

  • Astro as the static site generator. Content collections give Zod-validated frontmatter; the islands model means the default payload is pure HTML.
  • Vanilla CSS (~300 lines, custom properties) instead of Tailwind. The design is typography and whitespace; a utility framework would be a dependency with no job.
  • Mermaid as a lazy island: diagrams are authored as fenced code blocks, converted to <pre class="mermaid"> at build time by a 20-line remark plugin, and the renderer is dynamically imported only on pages that contain a diagram.
  • Pagefind for search: index generated at build time, WASM bundle loaded only on /search.
  • Cloudflare Pages for hosting, via Git integration: push to main, CF builds and deploys to the edge.

Alternatives considered

  • Next.js — built for dynamic applications; ships a runtime this site doesn’t need.
  • Hugo — unbeatable build speed, but the Go-templating story for embedding diagram components and custom Markdown transforms is weaker than remark plugins.
  • Eleventy — close second; would have required more manual wiring for type-safe frontmatter.
  • GitHub Pages — would force the repo public and serve under a /repo-name base path. Cloudflare Pages keeps the repo private and serves from a clean subdomain root.

Consequences

  • ✅ Publishing is git push. Build fails locally if frontmatter is malformed — errors surface before deploy, not after.
  • ✅ Every content page is static HTML; JavaScript is opt-in per page (theme toggle is the only site-wide script).
  • ⚠️ Mermaid renders client-side (~lazy chunk on diagram pages only) instead of build-time SVG. Acceptable trade; revisit with rehype-mermaid + Playwright in CI if it grates.
  • ⚠️ The *.pages.dev namespace is shared and first-come-first-served — stdout was taken, which forced the rename to unbuffered (see naming things).
  • ⚠️ No server means no first-party comments or analytics; anything interactive must be a third-party embed or nothing. Currently: nothing, by choice.