Skip to main content

ADR 0002 — No JavaScript framework

ADR 0002 — No JavaScript framework #

Status: Accepted · Date: 2024-01 (recorded retroactively 2026-04)

Context #

After ADR 0001 settled on Hugo, the next call: how do we handle the parts of the site that need to run code in the browser?

What we actually need browser-side:

  • Theme toggle (dark / light)
  • AI-badge popover open/close
  • Smooth-scroll on in-page anchors
  • Scroll-to-top button
  • Contact form: timing check, honeypot, base64 token, content filter

That’s it. No live updates, no client-side routing, no server-state rendering.

Decision #

Vanilla JavaScript, inline in layouts/partials/footer.html.

We rejected:

  • Alpine.js. ~15 KB minified for a couple of toggle handlers is a poor exchange. The “HTML-first” framing is appealing but the payload is real.
  • htmx. Designed around a server that can return HTML fragments. We’re a static site. The pattern doesn’t fit.
  • A SPA framework. Off the table given the site’s nature.

Consequences #

  • ✅ Total JS payload across the site is under 4 KB, all inline
  • ✅ No CDN dependencies → tighter CSP, no third-party SRI to track
  • ✅ No build step for JS; what’s in the partial is what runs
  • ✅ Onboarding cost: zero. A new contributor reads the partial and sees the entire client-side surface.
  • ⚠️ When interactivity grows beyond the current set, this decision needs revisiting. The trigger would be something like a form wizard, an interactive integration diagram, or a dashboard.
  • ⚠️ Inline scripts mean our CSP needs 'unsafe-inline' for script-src — accepted as a known trade-off until we move to nginx-emitted hashes.

Status #

Accepted, in production. Re-evaluate if interactive scope grows.