ADR-0086: Themes Restyle the Whole Public Site via Real Markup
Status: Accepted Date: 2026-06-26 Author: @johalputt Supersedes: none (extends ADR-0071 Theme Studio, ADR-0082 Theme Studio code editor)
Context
VayuPress ships a Theme Studio (ADR-0071) with a preset gallery and a token
compiler that emits CSS custom properties consumed by the public site. Built-in
"design" themes additionally ship a block of component CustomCSS that is
compiled into /theme.css alongside the token bridge.
Two structural problems undermined the promise that "applying a theme restyles your blog":
-
Dead component CSS. The design themes were originally authored against their own class prefixes (
.apex-*,.dispatch-*, …) — markup the public templates never emit. The public pages render a fixedvayu-*markup vocabulary (.vayu-hero,.vayu-post-list,.vayu-post-card,.vayu-prose,.vayu-footer-*,.vayu-author-box, …). The bulk of each theme's CSS therefore styled elements that do not exist, so applying a theme only recoloured the site (tokens) while the layout barely moved — the recurring "a theme switch only changes fonts/colours" report. -
Studio fragility. The Theme Studio page dereferenced the settings store without the nil-guard every other settings-dependent handler uses. When the store was not ready (startup race / init failure) the page panicked (HTTP 500), so the gallery "did not show anywhere" while the rest of the console kept working.
Decision
-
Themes target the real
vayu-*public markup. Every design theme carries a "Live public-site styling" section whose selectors match the markup the templates actually render, and that section now covers the whole page — navigation, homepage hero, post list/cards (including cover-image cards), the article body and headings, the author box, related posts, the comments section, and the multi-column footer — in that theme's own visual language. Applying a theme transforms every section, not just the homepage. -
Layout archetypes for colour presets. Colour-only presets are each assigned a reusable layout archetype (Minimal / Classic / Magazine / Editorial / Bold) — scoped CSS over the same
vayu-*markup — carried as thearchetypecustomization option, so even a palette swap changes structure and spacing, not just colour. -
The apply→persist→serve pipeline is authoritative. Applying a preset compiles the full token set including its
CustomCSS, persists the wholeTokensvalue as JSON (theme_tokensrow), and recompiles it into the in-memory/theme.csson both apply and startup. Switching themes swaps the served stylesheet with no stale CSS. -
The Studio degrades gracefully. Settings-dependent rendering in the Theme Studio handler falls back to defaults when the store is unavailable, so the page (and gallery) always render.
Guards
internal/theme/realmarkup_test.go— every theme that shipsCustomCSSmust style the real markup (.vayu-hero,.vayu-post-card).internal/theme/wholesite_coverage_test.go— every design theme must style the author box, footer columns, and cover-image cards, and no two design themes may compile to identical CSS.cmd/vayupress/theme_apply_distinct_integration_test.go— applying a design theme pushes its full layout CSS to the public/theme.css, and switching themes swaps it.cmd/vayupress/admin_os_theme_resilience_test.go— the Theme Studio renders (HTTP 200, gallery present) even with no settings store.
Consequences
- Deploying any built-in theme now visibly changes the entire public site.
- New design themes MUST include a whole-site
vayu-*section; the guard tests fail the build otherwise, making the old "recolour-only" regression impossible to reintroduce. - Themes are compiled into the binary, so theme changes require a rebuild and redeploy to take effect — unchanged from prior behaviour.