The design system source of truth for gbrain. Born from the de facto tokens
that landed in admin/src/index.css during the v0.26.0 admin SPA work and
formalized during the v0.36.1.0 Hindsight calibration wave's design review.
This doc is the calibration target for /plan-design-review and /design-review.
When a question is "does this UI fit the system?", the answer is here.
GBrain talks like a smart friend who knows your past, not a clinical scoring system. Every user-facing string passes through this filter:
- Second person, contractions allowed.
- Grounded in concrete data the user can verify ("2 of 3 missed" beats "Brier 0.31").
- Never preachy. Never "we recommend." Never "according to your data."
- Short. Under 25 words for narrative; under one line for status.
- Numbers grounded in real outcomes, never abstract metrics without translation.
Five surfaces use this voice (v0.36.1.0+):
pattern_statement, nudge, forecast_blurb, dashboard_caption,
morning_pulse. All five pass through gateVoice() in
src/core/calibration/voice-gate.ts with mode-specific rubrics. A Haiku
judge rejects academic-sounding candidates; up to 2 regens; then fall
back to a hand-written template from src/core/calibration/templates.ts.
CSS variables in admin/src/index.css. SVG renderer inlines literals
matching these tokens (src/core/calibration/svg-renderer.ts).
| Token | Value | Use |
|---|---|---|
--bg-primary |
#0a0a0f |
Page background |
--bg-secondary |
#14141f |
Sidebar, cards |
--bg-tertiary |
#1e1e2e |
Subtle surfaces, borders |
--text-primary |
#e0e0e0 |
Body text |
--text-secondary |
#888 |
Headings, labels |
--text-muted |
#777 |
Tertiary text — TD2 bumped from #555 for WCAG AA contrast (~5.5:1) |
--accent |
#3b82f6 |
Active states, links, primary CTAs |
--success |
#22c55e |
Healthy / ok status |
--warning |
#f59e0b |
Doctor warnings |
--error |
#ef4444 |
Failures, destructive confirmations |
Dark theme is the only theme. No light mode toggle planned — admin is an operator tool, not a marketing surface. Users live in the terminal with a dark theme already.
WCAG contrast:
- Body text (#e0e0e0 on #0a0a0f) → ~14:1, AAA
- Muted text (#777 on #0a0a0f) → ~5.5:1, AA (was 4.0 / fail before TD2)
- Accent links (#3b82f6 on #0a0a0f) → ~5.7:1, AA
| Variable | Value | Use |
|---|---|---|
--font-sans |
Inter, system-ui, sans-serif |
UI text, headings, body |
--font-mono |
JetBrains Mono, monospace |
Numbers, slugs, code, terminal-ish data |
Type scale (de facto, not formalized yet):
- 18px: sidebar logo / page title
- 14px: body
- 13px: nav items
- 12px: chart captions, secondary labels
- 11px: tertiary labels in dense charts
Numbers in tables and metrics use JetBrains Mono so column alignment is mechanical. Avoid mixing Inter and JetBrains Mono in the same line.
4 / 8 / 16 / 24 / 32px. Linear-app-style density: 24-32px between major sections, 16px between row groups, 8px within a row. The Calibration tab (approved variant-B mockup) is the canonical example.
- Sidebar 200px on the left. Active item gets a 3px left-border in
--accent. - Main content area uses the remaining width.
- Max content width: 720px for text-heavy pages (Calibration), 960px for data tables (Request Log).
- No 3-column feature grids. No icons in colored circles. No decorative blobs.
- Cards earn their existence — heading + content works without a card frame in most cases.
Server-rendered SVG via src/core/calibration/svg-renderer.ts. Pure
functions: data → SVG string. No DOM, no React component, no chart library.
XSS posture: server-side escapeXml() on every caller-controlled string.
Numeric inputs .toFixed()-coerced. Admin SPA renders via
<TrustedSVG> wrapper with dangerouslySetInnerHTML. Endpoint gated by
requireAdmin middleware.
Why server-rendered SVG (per D23):
- Chart logic stays close to the data math.
- Zero new client-side chart-library dep.
- SVG is accessible (text labels), scalable, copy-paste-friendly to PR descriptions and docs.
- Sets the precedent for future admin charts (contradictions trend, takes scorecard, etc.).
Four chart renderers in v0.36.1.0:
renderBrierTrend({ series })— sparkline + baseline reference at 0.25renderDomainBars({ bars })— horizontal accuracy barsrenderAbandonedThreadsCard(threads)— text rows + "revisit now" linksrenderPatternStatementsCard(statements)— clickable drill-down anchors
- Keyboard navigation is REQUIRED for all CLI interaction surfaces. The propose-queue review uses J/K/space/u/q shortcuts (gmail-style).
- Loading states: "Loading...". Don't show spinners on sub-200ms operations.
- Empty states ARE features: warmth + primary action + context. Cold-brain Calibration page tells the user EXACTLY how to build a profile, not "no data available."
- Error states: name what failed + name the next step. Never "an error occurred — please try again."
- Type scale formalization (current values are de facto, not enforced)
- Animation tokens (admin SPA has zero animations on purpose; v0.37 may add subtle progress / loading transitions)
- Print stylesheet
- Light mode (NOT planned — see "Dark theme is the only theme" above)
- Component library extraction (the React components live inline in admin/src/pages/;
no
<Button>/<Card>abstraction layer yet)
When adding a new UI surface to gbrain:
- Pick existing tokens before introducing new ones. New tokens go through
/plan-design-review. - Match the voice rules. Run candidates through
gateVoice()before shipping any user-facing string in the calibration surfaces. - Match the spacing scale and density. Linear-calm-clarity over dashboard-card-mosaic.
- Match the typography: Inter for UI, JetBrains Mono for numbers.
When updating this document: it's a living target, not a frozen spec.
Major changes go through /plan-design-review to keep the system coherent.