Frontend framework: Astro
- Status: Accepted
- Date: 2026-06-06
- Deciders: Engineering, Design Systems
Context
Section titled “Context”quarry.team is a content-and-craft marketing site: a homepage, services, a curated case-study library (/work/<slug>), an editorial blog (/blog/<slug>), an about/team section, and lead-capture forms (contact, careers). The dominant requirements are:
- Best-in-class Core Web Vitals. The site is itself an argument for our engineering taste; near-zero unused JavaScript on content pages is the target (see
docs/brd/non-functional-requirements.md). - Content-first authoring via typed, git-based collections (MDX + Zod) — see ADR 0004.
- A real design system sourced from design tokens — see ADR 0003 and ADR 0007.
- Static output deployed to Cloudflare’s edge — see ADR 0006.
- Islands of interactivity where genuinely needed (mobile nav, lightweight form widgets, search UI) without shipping a SPA runtime to every visitor.
The legacy site is Gridsome (Vue 2). Gridsome is effectively unmaintained, Vue 2 is end-of-life, and the architecture couples content to a WordPress instance we want to retire.
Decision
Section titled “Decision”We will build the marketing site (apps/web) on Astro with TypeScript in strict mode (Astro 5 at the time of this decision; the project tracks the current major and now runs on Astro 7 — routine version currency, not a change of framework).
Rationale:
- Zero-JS by default, islands on demand. Astro ships HTML/CSS and hydrates only the components we explicitly mark, which maps directly onto our CWV budget. Most pages ship no framework runtime at all.
- First-class content layer. Astro Content Collections give us typed MDX + Zod schemas out of the box — the exact content model we want (ADR 0004) — with no separate CMS to run.
- Static-first, edge-friendly. Pages prerender to static HTML, deployed as a single Cloudflare Worker via the
@astrojs/cloudflareadapter (ADR 0006); the only on-demand routes are the same-origin/api/*form endpoints.@astrojs/sitemapand Pagefind fit cleanly. - Framework-agnostic islands. If we ever need a React/Svelte/Vue island, Astro supports it without committing the whole site to one runtime.
- Authoring ergonomics.
.astrocomponents are close to HTML, which keeps the markup honest and the design-token mapping legible.
Consequences
Section titled “Consequences”Positive
- Content pages ship essentially no JavaScript; CWV headroom is large.
- The content model, framework, and search are one coherent toolchain.
- TypeScript strict +
astro checkcatches content-schema and template errors at build time.
Negative / costs
- Astro’s island model is a different mental model than an SPA; complex client-side state is deliberately awkward (acceptable — this is a marketing site, and the controlled dynamic tier is just the same-origin
/api/*form routes on the Worker, backed by D1, ADR 0014). - Smaller ecosystem than Next.js for app-style features we don’t need here.
Alternatives considered
Section titled “Alternatives considered”- Next.js. Excellent, but optimized for app-shaped React products. Reaching the same “no unused JS on a content page” baseline requires fighting the defaults (RSC payloads, hydration). Heavier than the problem.
- Remix / React Router. Server-rendering-first; great for dynamic apps, but we want static output and minimal client JS. Wrong center of gravity.
- SvelteKit. Closest competitor on the “lean output” axis and genuinely good. Astro wins on the content-collection story and the explicit islands-everywhere-else model for a multi-framework-tolerant marketing site.
- Keep Gridsome (Vue 2). Unmaintained, EOL runtime, coupled to WordPress. Non-starter.