Styleguide
The reference and the regression test. Every token, control and pattern the product is allowed to use.
Colour
Colour is semantic, never decorative: one meaning per hue, and a fifth meaning needs a fifth hue added to globals.css rather than a raw Tailwind palette colour picked at a call site. Each hue has two fill weights and picking the wrong one is the bug — -bg is chip weight (a 20px pill), -wash is surface weight (a fill behind text). Never fake a wash with a chip fill at an alpha.
Not a status — the warm serif tone for hero and greeting copy.
Type
Three faces, one job each. Zodiak for headings — the page title is bold italic in the heading accent, sampled from Figma; card headings (h2–h3, 600) haven't been sampled yet and still take the base layer's plain default — Satoshi for interface, mono for data — every number, ID and timestamp, always tabular via .money. Labels use .eyebrow.
Page title, 40/40
Card heading, 15/600 — unconfirmed
Body copy is 13px on a 1.6 line height. Rows are 13px/600 with an 11px mono sub-line naming the record they hang off.
Controls
One ink-filled primary action per card — the brand hue is identity, not a button fill. Chips are mono 10.5px on a tinted fill with no border, and they come from Badge: never hand-mix inline-flex … font-mono text-[10.5px]. Every control gets its colour transition and its pointer cursor from the base layer, so a bare transition-colors already complies.
Surfaces
A card takes a 1px line ring and no shadow. The one exception is .doc-sheet, for a document a reader is asked to sign — elevation and radius are theme, so the next surface that wants to be paper uses that rather than spelling its own shadow. Amounts right-align and never wrap. Empty states are dashed boxes that say what creates the record.
Recent
4Nothing yet
A rail panel says this instead of a dashed box.
| Record | Owner | Status | Value |
|---|---|---|---|
HF Halcyon Freight Logistics · Brisbane | Ada Lovelace | paid | $41,000 |
NL Northwind Labs Research · Perth | Grace Hopper | awaiting reply | $9,500 |
--bloom-size; a surface carrying imagery paints its own backgroundProportion
A quantity is a bar, and the bar arrives from zero — a bar simply “there” on load reads as a rule under the numbers. The fill comes from lib/accents (POSITIVE_BAR, NEUTRAL_BAR), never a raw colour, and the number is always stated beside it, which is why the bar itself is aria-hidden. “How far along” has one definition: lib/progress gives the share, whether it is complete, and which fill a completion bar takes. Positive means it happened, so the bar only turns positive at the end.
Motion
One curve for everything: --ease-soft at 150ms, which is also Tailwind's default — so don't spell duration-150 ease-soft, spell the exceptions (120ms for a drop-target highlight, 300ms for a marker travelling, 700ms for a bar filling). Content that mounts once uses an animate-*-in token; anything with a data-state and a matching exit uses the tw-animate-css utilities instead. Reduced motion zeroes each keyframe's movement variable rather than switching animation off, so spinners keep spinning.
These replay on demand, which is what makes this section a test: press Replay and either they move or the theme is broken.
The field group that replaced its sibling, unfolding downward.