Design #
Visual system for Quantum. Register: product. Personality: calm precision — ink on good paper, composed and legible. All colors OKLCH.
Theme #
Scene: a couple reviews the month on a laptop at the kitchen table under evening
lamplight; each also glances at balances on a phone in daylight. Both themes are
first-class: light is the canonical "paper statement" reading, dark is the
evening companion. Default follows prefers-color-scheme, with a data-theme
override on <html> persisted per user.
Identity holds across themes: warm ink on warm paper (light), warm chalk on warm charcoal (dark). Never pure black or white.
Color #
Strategy: Restrained. Tinted warm neutrals carry the interface; one spruce accent under 10% of any surface; semantic and chart colors appear only where data demands them.
Neutrals (warm, hue ≈ 85) #
| Token | Light | Dark |
|---|---|---|
--bg |
oklch(0.975 0.004 85) |
oklch(0.19 0.006 85) |
--bg-raised |
oklch(0.99 0.003 85) |
oklch(0.225 0.007 85) |
--bg-sunken |
oklch(0.955 0.005 85) |
oklch(0.165 0.006 85) |
--text |
oklch(0.245 0.012 85) |
oklch(0.92 0.008 85) |
--text-muted |
oklch(0.49 0.012 85) |
oklch(0.70 0.01 85) |
--text-faint |
oklch(0.62 0.01 85) |
oklch(0.55 0.01 85) |
--border |
oklch(0.90 0.006 85) |
oklch(0.30 0.008 85) |
--border-strong |
oklch(0.82 0.008 85) |
oklch(0.38 0.01 85) |
Accent — spruce (hue ≈ 190, deliberately not money-green, not fintech navy) #
| Token | Light | Dark |
|---|---|---|
--accent |
oklch(0.46 0.07 190) |
oklch(0.80 0.08 185) |
--accent-hover |
oklch(0.40 0.075 190) |
oklch(0.85 0.075 185) |
--accent-bg |
oklch(0.94 0.02 190) |
oklch(0.27 0.03 190) |
--on-accent |
oklch(0.98 0.005 190) |
oklch(0.20 0.02 190) |
Semantic (paired with sign/shape, never hue alone) #
| Token | Light | Dark | Use |
|---|---|---|---|
--pos |
oklch(0.52 0.10 175) |
oklch(0.78 0.11 170) |
income, inflow |
--neg |
oklch(0.53 0.13 30) |
oklch(0.74 0.13 30) |
expense, outflow, destructive |
--warn |
oklch(0.60 0.11 75) |
oklch(0.80 0.11 80) |
staleness, needs-attention |
--warn-bg |
oklch(0.95 0.03 85) |
oklch(0.26 0.03 80) |
banner fills |
Red (--neg) appears in chrome only when money or data is at risk; an expense
figure in a table is plain ink, not red.
Chart colors #
--chart-1oklch(0.55 0.13 180)light /oklch(0.65 0.12 190)dark — the single-series mark color (net worth line). Validated ≥0.1 chroma and 3:1 contrast per surface. (--chart-2…7remain as spare series tokens.)- Per-category palette
--cat-0…--cat-11: each category owns a stable palette index (categories.color_index, assigned at creation, never recomputed from position). Generated with poline viascripts/generate-category-palette.ts— a closed-loop hue journey anchored at spruce/amber/plum, normalized into OKLCH L 0.54–0.62, C ≥ 0.10 so one set passes the lightness band, chroma floor, and 3:1 contrast on BOTH surfaces (no dark override). Neighboring slots are interleaved to maximize hue distance; charts additionally separate fills with 2px surface gaps and always carry a legend, so identity never rests on hue alone. Neutral (--text-faint) is reserved for "Other" and "Uncategorized" — never a category.
Typography #
- UI + numerals: Inter Variable (bundled via
@fontsource-variable/inter, self-hosted, no CDN). Fallbacksystem-ui. - Monospace (DIDs, rule patterns, tokens):
ui-monospace, 'Cascadia Mono', Consolas, monospace. - Tabular figures are law: any column of money or dates sets
font-variant-numeric: tabular-nums. - Scale (1.25 ratio): 13 / 14 (body) / 16 / 20 / 25 / 31 px as
--text-xs/-sm/-md/-lg/-xl/-2xl. Weights: 400 body, 500 emphasis & nav, 600 headings & figures. Line height 1.5 body, 1.2 headings. - Body measure ≤ 70ch. Money aligns right; descriptions align left.
Spacing, shape, elevation #
- 4px base:
--space-1..8= 4, 8, 12, 16, 24, 32, 48, 64. - Radius:
--radius-sm5px (inputs, chips),--radius-md8px (surfaces, menus), full for provenance badges. - Elevation by border first: 1px
--border+--bg-raised. Shadows only on overlays (menus, dialogs):0 4px 16px oklch(0 0 0 / 0.10)light,/ 0.35dark. No decorative shadows on static content.
Motion #
120ms (micro: hover, check, chip) and 180ms (panels, menus), both
cubic-bezier(0.22, 1, 0.36, 1). Nothing bounces, nothing floats, no layout
property animation. prefers-reduced-motion: reduce collapses all to 0ms.
App shell #
- Desktop (≥880px): fixed left sidebar, 216px. Wordmark "Quantum" top; nav: Dashboard, Ledger, Reports, Accounts, Settings; footer shows sync status line ("Synced 6:02 AM") and the signed-in user's handle. Content area scrolls independently; reports/forms cap at 1040px, the ledger runs fluid.
- Mobile (<880px): bottom tab bar with the same five destinations; page title in a slim top bar. No hamburger menus.
- Login is shell-less: a centered column on
--bg, wordmark, one input, one button.
Component conventions #
- Provenance badge: pill,
--bg-sunkenfill, 12px text:⚙ rule/person's handle/↻ carried. Click opens event history. - Banners (connection health): full-width strip under the top of content,
--warn-bgfill, 1px--warnborder (all four sides), plain sentence + one link out. No icons larger than the text. - Tables: row hover
--bg-sunken, 1px hairline row separators, header 13px/500/--text-muted, uppercase avoided. - Empty states: one sentence + one action, set in
--text-muted. No illustrations. - Focus: 2px
--accentoutline, 2px offset, on every interactive element.