Skip to content

0018: Console design system: brand tokens, self-hosted fonts, no build step

Date: 2026-10-12 · Status: accepted

Context

The console (ADR 0010) worked but looked like a default admin page. The Tenax brand kit (docs/brand/) defines colours, type and the timeline motif. The console must stay a server-rendered HTMX app: no SPA framework, no build step, no CDN, CSP default-src 'self', and page p99 under 50 ms.

Decision

  • One stylesheet, brand tokens. internal/web/static/app.css copies the semantic tokens of docs/brand/palette.css (keep them in sync) and stays under 40 KB. Light by default, dark from prefers-color-scheme, and <html data-theme> overrides either way.
  • Theme toggle. static/app.js (loaded in <head> so the saved theme applies before first paint) stores the choice in localStorage inside try/catch. It is the only script besides HTMX and its SSE extension; there are still no inline scripts.
  • Fonts are embedded, latin subsets fetched once from Google Fonts: Inter (variable 400–700, UI), JetBrains Mono (variable 400–600, IDs, timestamps, SQL) and Bricolage Grotesque 600 (headings). About 100 KB in all, served with immutable caching; the browser makes no external request. Licences: static/fonts/LICENSE.txt (SIL OFL 1.1).
  • Asset URLs carry a content hash ({{asset "app.css"}} → /static/app.css?v=…), so the immutable caching of /static/ never serves a stale stylesheet or script after an upgrade.
  • Shell. A dark sidebar with the Stake mark and wordmark (inline SVG from docs/brand/), a sticky header with the namespace, the user and the theme toggle. Status is shown with pills (healthy/warning/error, running) and the PITR window as an ember bar; the latest restorable point is the cyan "instant" marker.
  • The overview stays cached HTML. Its rows are still written by the direct writer; "Recent activity" loads after the page from /activity?fragment=1&recent=1, so activity changes do not invalidate the cached overview.

Consequences

Restyling means editing one CSS file and the templates; there is nothing to build. New fonts or weights cost bytes on every first visit, so add them only with a reason. Pages may use style attributes for computed positions (the CSP allows inline styles, not inline scripts).