ADR-0071 — Theme Studio: A Safe, Token-Driven Theme Editor that Surpasses Tumblr
Status: Proposed Date: 2026-06-20 Deciders: VayuPress Maintainers Target release: v1.4.0
Context
VayuPress already has a token-driven theme console: a palette stored in settings
(theme.primary_*, theme.accent_*), operator CSS served at /theme.css (never
inlined, so style-src 'self' holds), favicon/logo upload, a WCAG AA contrast
advisory, and JSON export/import. The next goal is a full Theme Studio that
surpasses Tumblr — the reference point for "powerful theme editor" — in
technology, design, and simplicity at once, without breaking any invariant.
Tumblr's editor exposes a single raw-HTML template box ({block:Posts}) that can
inject arbitrary <script> — an XSS surface, no design consistency, no
accessibility floor, code-only. We must do better while staying sovereign and
CSP-strict (no unsafe-inline, no unsafe-eval, no third-party scripts).
Decision
Build Theme Studio on three inventions: a safe declarative template engine, a design-token system, and a dual-mode (visual + code) editor with live sandboxed preview — all server-rendered, all sanitised, all portable.
Safe template engine (internal/themelang)
A constrained, declarative template DSL — not raw HTML-with-JS and not arbitrary Go templates with filesystem/reflection access. A small AST supports blocks, loops, conditionals, and variables over a read-only exposed data model (posts, tags, site, pagination, current view). Compiled and rendered server-side; output is sanitised. Script injection is impossible by construction — strictly more expressive than tokens, strictly safer than Tumblr's raw HTML.
Design tokens
A typed token system (color, type-scale, spacing, radius, shadow, motion)
compiles to CSS custom properties served at /theme.css (never inline). The
editor enforces WCAG AA contrast at edit time (extending the existing
advisory into a gate). Themes are portable JSON bundles (extending the current
export/import), seeding a future sovereign theme marketplace.
Editor experience
- Dual-mode: a visual token/section editor for everyone, plus a code view of the template DSL for power users. Tumblr is code-only; we are both.
- Section/layout model: composable sections (header, post-list, single, footer, custom) with per-section options and drag-to-reorder.
- Live preview: a
sandbox-ed iframe renders the real site with the draft theme via a preview token; hot-reload over the existing SSE channel; responsive breakpoint toggles (mobile/tablet/desktop) and light/dark, with real content. - Versioning & safety: draft vs published, theme version history, one-click revert, reset-to-default.
- Intelligence: a local-Ollama theme assistant (palette/typography from a prompt or an uploaded logo) and an automatic accessibility/performance linter (contrast, font-loading, CLS hints) — suggest-only, never auto-applied.
- Performance: a theme compiles to one minified CSS plus at most one
nonce-gated JS file; introducing
evalor a third-party script is impossible by construction → Lighthouse-perfect themes by default.
Storage
themes table (migration 029): versioned bundles, draft/published state,
per-theme token + template + section data.
How it surpasses Tumblr
| Dimension | Tumblr | VayuPress Theme Studio |
|---|---|---|
| Safety | arbitrary JS (XSS) | safe DSL, no script injection possible |
| Editing | code-only | visual and code dual-mode |
| Consistency | freeform | design-token system → CSS custom props |
| Accessibility | none | WCAG AA contrast gating at edit time |
| Preview | basic | sandboxed live iframe, responsive + light/dark, real data |
| Portability | per-account | portable JSON bundles |
| Intelligence | none | local-LLM assist + a11y/perf linter (suggest-only) |
| Performance | variable | one minified CSS; eval/3rd-party-script impossible |
| Sovereignty | hosted SaaS | single binary, zero CDN, strict CSP |
Threat model
- Template injection.
internal/themelangexposes only a read-only data model and a fixed instruction set — no filesystem, network, reflection, or arbitrary function calls. Output is sanitised; the no-raw-HTML invariant holds. Fuzz-tested against escape/injection payloads. - Preview isolation. The live preview iframe is
sandbox-ed and served same-origin with a preview token; draft themes cannot affect the live site or other sessions. - CSS injection. Operator CSS continues to be served at
/theme.css, never inlined; token compilation emits only custom-property declarations.
Consequences
Positive: a theme editor that is safer, more accessible, more consistent, and simpler than Tumblr's, while remaining sovereign and CSP-strict; portable themes; AI-assisted design; Lighthouse-perfect output by construction.
Negative / costs: the safe template engine is the most security-sensitive component in the project and requires a dedicated security review + fuzzing; the visual editor + live preview are substantial UI work (phased separately from the backend engine).
Alternatives considered
- Let operators write raw HTML/JS themes (Tumblr model) — rejected: XSS, CSP violation, no accessibility floor.
- Tokens only, no templating — rejected: insufficient layout power for a "surpass Tumblr" goal.
- Embed a third-party templating language — rejected: dependency surface and weaker sandboxing guarantees than a purpose-built read-only DSL.