The console, as it is drawn
Every sample here is the real element the console uses, rendered live in the theme you pick above. Change the token or the component and this page changes with it. The words come from CLAUDE.md › Design; the values from globals.css.
Flowing, converging
The console should feel like the logo — strands that flow and meet at the important point — without ever drawing the logo. Charts converge on a total; lines end in “now”; lists point at the thing they describe.
Every element carries data
A curve, a fade, a ring or a dot exists because it encodes a fact (age, share, state, margin). If it would look the same with different data, it is decoration and it goes.
Replace, never add
A new element takes the place of something that was there. Chrome and duplication can be removed; capability cannot — every measure, dimension, window and exact figure of what it replaces survives.
One vocabulary
One fact looks the same everywhere: the same dot for a node's state on Fleet, on the dashboard and in a list; the same purple for money; green only for health.
Colours#
The neutrals and the brand hue, read from the live CSS variables — switch the theme above and the values change. Purple is ours; lime belongs to the dark theme's action role and is never used directly in new code.
Accent roles#
Colour an element by what it means, not by hue. Each role is a token that follows the theme; in the dark theme action and live are lime, in the light theme they are purple — the meaning is the constant.
Money is earn at three weights — settled solid, pending lighter, the open epoch's estimate lightest — so a chart of earnings is one hue, never three. Green is reserved for a node being healthy.
Typography#
“Editorial money”: a serif for the statements — page titles and the total a flow converges on — and a quiet grotesk for the work. Two families; mono only where a value is an identifier.
Your nodes, the pools they serve, and what it adds up to. Earnings include $56.70 estimated from the open epoch.
| Role | Face | Used for |
|---|---|---|
| font-title | Newsreader 500 | page titles and statement figures — the total a flow converges on |
| font-heading | Hanken Grotesk 600 | card and section headings |
| body | Hanken Grotesk 400 / 500 | everything else: tables, controls, captions |
| font-mono | JetBrains Mono 400 | ids, tags, code — never for prose or headings |
Brand#
The symbol is two lobes converging on a point, drawn as an outline whose fill fades toward the point. Beside the wordmark it is purple and the wordmark is ink; alone, it is the favicon. The glowing illustration stays for hero, loading and deck moments.
| Asset | Use |
|---|---|
| public/brand/symbol.svg | the UI symbol; BrandSymbol draws it in currentColor, default text-syn-purple |
| public/brand/wordmark.png | the logotype; Wordmark draws it as a mask in the text colour |
| app/icon.svg · favicon.ico · apple-icon.png | favicons — keep them in middleware's allow-list |
| public/brand/logo.png · AnimatedLogo | the illustration: hero, loading, deck and social only |
Motion#
Things arrive; they do not appear. Motion runs once per new picture, toward the point, and is never the only way a state is shown.
Once per new picture
An arrival plays when the data or the axes change — never on hover, never on resize, never in a loop.
Toward the point
Strands draw from their source into the total; figures count into their new value; the highlight stretches to the new option and settles.
Nothing half-drawn
Under prefers-reduced-motion, and in a background tab, the finished picture shows at once; a timer guarantees it.
Short
280 ms for a control, 700 ms for a figure, 1.4 s for a whole flow. Nothing waits on motion.
Surfaces#
One card (cardCls): a hairline border, the surface colour, a soft shadow, 12px radius. Blocks inside a band are divided by strands that fade at both ends, not by boxes. A page has one elevated surface — the money.
Strand switch#
The one picker (Segmented). The highlight is a single element that stretches toward the new option, then its trailing edge catches up and it settles — you see where the choice came from. Every window picker runs 6h · 1d · 7d · 1m · 6m.
Marks#
One dot vocabulary for a node's state, wherever it appears. The heartbeat dot's ring widens and fades as the last heartbeat ages toward the stale threshold; the same shapes name states in lists.
Figures#
A figure counts into its new value when the window changes, so the eye sees how far it moved. Hover readouts are instant. The first render shows the value as-is.
Badges#
Tinted, never loud: a tone at low opacity with its own foreground. Pair labels (a model's state on a node) use the earn hue for earning and waiting, red for held.
Flows#
Parts flow into a total. Band thickness is the value; the largest come first; the rest merge into “N others”; hovering a band reads its amount where the total sits. The dashboard's FlowBoard does this twice over — requests into nodes into pools into earnings.
Trend to now#
A window's history as one smooth strand that ends in a dot at “now”, under the figure it explains. The availability board does the same with each node's history running into its heartbeat dot.
Rules#
Two themes, one system
Supply defaults to light; finance to dark; a stored Appearance choice wins. Every syn-* class follows the theme through CSS variables — never hard-code a hex.
Roles, not hues
Colour an element by what it means (action, select, live, link, earn, ok, warn) — not by which colour looks nice. Green means health only. Purple means ours, and money.
The .js config is live
Tailwind resolves tailwind.config.js; keep tailwind.config.ts byte-identical or new classes silently fail to generate.
Keep the counts honest
A figure beside a chart is the chart's own total. Estimates are labelled while any estimate is in them. A number never appears where it would clash with a glyph.
Chrome can go, capability cannot
Boxes, tiles, duplicate bells and toggles that a picture makes redundant are removed. Measures, dimensions, windows, filters, tooltips and test ids stay.
Ask before drawing
When something reads as cheap, find out whether it is the shape, the flatness or the placement before drawing another shape.
Where things live#
| Path | Holds |
|---|---|
| CLAUDE.md › Design | the principle and where things live |
| app/globals.css | themes, tokens, accent roles, chamfer, rail |
| tailwind.config.js | syn-*, brand-*, lime-*, surface-* classes (the .ts copy is identical) |
| public/brand/symbol.svg · components/BrandSymbol.tsx | the UI symbol, drawn in currentColor |
| components/supply/record/primitives.tsx | Segmented (strand switch), buttons, Badge, cards, Section |
| components/supply/fleet/HeartbeatDot.tsx | HeartbeatDot, StateMark |
| components/supply/CountUp.tsx | figures that count into place |
| components/supply/dashboard/FlowBoard.tsx | requests → nodes → pools → earnings |
| components/supply/Confluence.tsx | parts flowing into one total |
| components/supply/fleet/OverviewStrip.tsx | the Fleet summary band, NowLine |
| lib/highlight.ts · lib/rail.ts · lib/theme.ts | node highlight, rail collapse, appearance |
Everything on this page renders from the codebase; there is nothing to keep in sync by hand except these words.