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'forscript-src— accepted as a known trade-off until we move to nginx-emitted hashes.
Status #
Accepted, in production. Re-evaluate if interactive scope grows.