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:
- Publishing friction ≈ zero. Writing happens in Markdown in a local editor; a push must be the entire deploy pipeline.
- Zero infrastructure to babysit. No database, no server, no CMS.
- Fast and honest. Static HTML, no client-side JavaScript unless a page earns it.
- 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-namebase 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.devnamespace is shared and first-come-first-served —stdoutwas taken, which forced the rename tounbuffered(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.