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.csscopies the semantic tokens ofdocs/brand/palette.css(keep them in sync) and stays under 40 KB. Light by default, dark fromprefers-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 inlocalStorageinside 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).