PRESTON · THE DESIGN SYSTEM
One face, and the two layers it comes in.
Every value on this page was measured off the element beside it, after paint, from this document. Nothing here is retyped from the token file — so if a token changes, the page changes, and if the app stops reading a token, the page stops showing a value rather than printing the one it remembered.
Preston draws with one mark, one type ladder, one palette and one rhythm, on every surface without exception. On the surfaces that show understanding it draws with a second vocabulary on top, where each choice carries a fact. Those are two different promises, and the second is the one that gets copied where it does not belong.
The boundary
Coherence is not sameness. The climb and the review pages work because their visual choices carry meaning: colour encodes kind, fill encodes standing, a slash means no-longer-stands, №n means ordinal position in a real sequence. Copy that onto a pricing page and the meaning becomes decoration — and afterwards nobody can tell which choices are load-bearing. That is the usual way a design system dies.
LAYER ONE · THE FOUNDATION
Universal. No exceptions.
the mark · the type ladder · the palette · the rhythm · the voice
Marketing, auth, docs, the desk, admin, legal, error states, empty states. Nothing about persuasion requires a different diamond, and nothing about a login form requires losing the header.
LAYER TWO · THE SEMANTIC GRAMMAR
Only where there is understanding to encode.
kind colour · the shape axis · standing fill · the slash · ordinals · lenses · receipts
A settings form has no kinds and no standings. Applying this there is the same category error as the drift it was meant to fix, pointed the other way.
The sanctioned exceptions
Three, named here so they read as decisions rather than as drift. There is no fourth, and a fourth would be written down in the token file before it was drawn.
text-headingandtext-displaysit above every witness role and are the whole licence. A 19px landing headline is not coherent, it is mute. They never appear on a surface that shows understanding.- The witness ladder is a scanning rhythm for one-sentence claims. Multi-paragraph clauses set at 14.5px would be measurably harder to read, and legal text is the one place a reader is presumed to have read it. Legal converts its mark, eyebrow, radii and badges only.
components/uirenders a claim axis, and a claim has no standing to encode as fill. Forcing the ring there would draw a fact that does not exist. Only its chip metrics and its openable-line treatment unify.
Layer one · the foundation
The mark
One component draws it, and it is the only way a wordmark reaches a page. There were three, and a visitor could meet all of them in two clicks. The diamond is --kind-promise and the wordmark is --ink: both are theme tokens, so the mark inverts with the app and there is no second asset and no dark logo.
The type ladder
Sixteen roles and two sanctioned exceptions, named by the job each one does. The rule for choosing is to ask what the text IS, never how big it should be — and if no role fits, the honest move is to add one to preston-tokens.css with a reason. This is the ladder the face was proved on; until it had names it was ten pixel sizes stamped ~714 times, which is not a system but twenty-one files behaving well. The third column is what each specimen actually renders at, so a role that declares no leading of its own reports the one it inherits — the line under each role name is the exact set of properties that role declares.
the trailing stamp: a time, an arrow, a count
riosgabriel/relay
an identity string, read for recognition rather than for meaning
HOW THIS FORMED
names a section or a field inside a surface
the row's naming line — the ordinal, the change word, the standing
TENSION
a kind's own name — never rendered without the kind's hue beside it
PRESTON · THE DESIGN SYSTEM
names the surface you are on
outbox discipline → idempotent replay
running mono content: receipt paths, ledger lines, lens rows, ticks
Point your agents at this
a control's label
return withIdleAndWallTimeout(run, budget);
a line of code AND the gutter that numbers it — one leading, so they finally align
PRESTON
the product signing its name
A read that starts at package.json and goes down from there.
code set inside a sentence — the one role measured relatively, because its job is to be a little smaller than whatever it interrupts
held loosely — could change as the read deepens
the explaining line beneath a claim
Ledger, outbox and bounded retries compose into one delivery guarantee.
the sentence a unit exists to carry
A passage read in paragraphs rather than scanned in rows — looser leading, a longer measure, and the one place a reader is presumed to have read every word.
a passage read in paragraphs
What Preston won’t say
a surface's own title
Preston has never read this repository.
the one sentence a surface is built around — and the top of the non-persuasion ladder
One face, two layers
PERSUASION ONLY — a marketing, legal, auth or admin page's own title
An engineer who reads first
PERSUASION ONLY — the landing hero, and a docs page's h1
Tracking
One ladder, and its steps are semantic rather than decorative: the wider the tracking, the further out the label stands from the content it names. Most roles bake their own step; these five are for when a role's size is right and its tracking is not.
The palette — neutrals
Neutrals carry no meaning of their own. They say how far forward a surface sits and nothing else, which is exactly why a neutral may be used anywhere and a kind colour may not.
the page itself — the ground every surface sits on
a navigation rail, standing beside the content rather than under it
the line where a rail stops and the content begins
a surface lifted off the page — a card, a panel holding one claim
a quieter surface than a card: the docs rail, the desk sidebar
the ground under code — a diff, an excerpt, an evidence chip
the ordinary hairline: a section rule, the border around a card
a hairline INSIDE a surface, quieter than the one around it
the divider between two rows of one list
a row under the pointer, and the row that is currently selected
a dashed or provisional edge — a boundary not yet settled
the backdrop under a sheet or an inspector — the page pushed back, so the sheet is what is being read
The ink ramp
Six steps, and the step is the meaning: how dark a piece of text is says how much of the reader's attention it is asking for. Each line below is printed in the ink it describes.
The kind families
Colour marks consequence — what it costs to be wrong about this — and never where the claim came from. Each family is a triad: an accent for the label, a tint for the body, a hairline that sits on the tint. lib/kinds.ts is the only place a kind becomes these classes.
it bites. The one flag, and rationed — red is the colour a reader must not learn to skip.
unresolved. Something missing, something in disagreement, or a rule observed rather than decreed.
seen and held — a discipline recognized in the code, a leaf of evidence, a thing that can be acted on.
a relation drawn between two things already on the record. The mark is this colour.
a synthesizing read that names a centre — an orientation, a composition, a decision quoted verbatim.
the ink that goes on the owned fill — the primary door's own foreground. Every other text colour comes off the ink ramp and inverts with the theme; this one has to stay legible on a fill that does not, which is why it is a token and not a judgment call at the call site.
Context origins
Provenance, not kind. Where an instruction came from is a different question from what it costs to be wrong about it, so it is a different axis with its own hues — deliberately outside the kind namespace, so a CODE origin is never mistaken for a source claim.
Edges
Relationship temperature, independent of the two nodes an edge links. A because-of edge is teal whether it runs between two risks or two decisions: the colour belongs to the relation, not to its ends.
Shape and elevation
Two radii, and only two. Ten shipped before this — four of them a second parallel ladder — which is how a panel and the chip inside it ended up rounded four different ways on one screen.
a control or a tag — something you press or read as one unit
a surface — something that holds other things
not a third step: the corner softening on a 10px diamond, and only preston-mark.tsx may read it
a panel that has come forward — an inspector, a sheet
a card resting on the page
Measure
Four widths, named by what goes inside them. Ten shipped before this. The bars below are the real containers; the reading is what each resolves to right now, which is why the last one is a character count and not a number anybody chose.
the page's outer bound
the reading column — a hero, a claim, a paragraph of the record
a short standalone block that should not run the full column
continuous prose, bounded in characters rather than pixels, because a reading measure is a character count
The two families
Two, and there is no third. If the eye should verify it, it is mono.
The rhythm
The one part of the foundation with no role names. It rides Tailwind's 4px step, half-steps allowed, and the six intervals below are the ones the app actually uses. Saying that plainly is better than implying a naming that does not exist — and this row is where the next set of names will come from.
The doors
Three tiers and one geometry. The primary is teal because a button is an ACT and teal is owned — blue is the mark. Five primaries shipped before this in four different foregrounds, which is the tell: nobody chose blue-on-something, each site chose a light colour locally and moved on.
One deviation from the witness row’s geometry, written down rather than drifted into: min-h-11. The row’s px-4 py-2 around an 11px label is about 32px tall, which is under the touch target — fine on a desk surface reached with a mouse, not fine on the two buttons that carry every signup on a phone.
The one empty state
One component, one shape, and the only form an error, a refusal or an empty list takes anywhere in the app. There were seven, from 13px to 38px, and two of them wrapped themselves in a bordered card so the same sentence was furniture on one route and a hero on another.
Three parts and no fourth: a label naming the situation, one sentence, and at most one way onward. No kind colour, no standing, no ordinal — a session that expired is not a claim, and dressing it as one is the category error above, pointed the other way.
Layer two · the semantic grammar
Three axes, three jobs. Colour marks consequence, shape marks kind, and fill with the slash marks standing. Only the first of them disappears for a colour-blind reader, which is the whole reason the second exists.
Kind, said twice
The shape axis is not invented here. /u/:owner/:repo/standards shipped four of these for exactly this reason, and converting that page onto the standard would have deleted a working colour-blind-safe encoding in the name of coherence. Promoting it instead makes the standard better rather than merely more widespread. All five glyphs come from one Unicode block.
The hollow variants (◇ ○ □) are deliberately unused: fill is already spoken for by standing, and a shape axis that borrowed it would say two things with one mark. decision’s ▾ is one of the two shapes this adds — a promise ▸ points forward at what it will keep, a decision ▾ comes down.
The circle is the other. owned used to be ●, and then the ring below became the app’s state mark — whose solid form is a filled circle meaning earned. One shape, two vocabularies: an evidence chip read ● BECAUSE OF a scroll away from a relay teaching that ● is what a ratified judgment looks like. The circle belongs to state, so the kind axis gave it up. ▮ is the quotation bar — a source is what a claim rests on — and it is the shape furthest from ▸, which matters because promise-blue against owned-teal is exactly the pair a deuteranope flattens.
Standing, and the stream
The ring in every state it has, drawn by the component the climb and the review pages use. Hollow means not yet earned, solid means earned, the inner red dot means two live readings disagree, and the slash means no longer stands — kept on the record, no longer binding the read.
outbox.enqueue before the transport sees it.replay() is idempotent under a partial flush.Row 2 carries a code span because authored() handed the renderer a sentence with backticks in it, not because this page styled one. Preston’s backend injects those ticks deterministically after every prose pass, so a surface that prints his sentence raw prints backticks at the reader — which is why the slot is typed and a plain string does not satisfy it.
№n is display-derived: the caller’s own 1-based position in the list it is showing, never a stored identity. Two readers looking at different slices see different numbers for the same event, and that is correct.
The standing vocabulary
One map, rendered from the function that owns it — so the words on this page cannot disagree with the words on the climb.
- held loosely
- held loosely — could change as the read deepens
- stands
- independent support attached — direct, but revisable
- ratified
- confirmed by the team, not only by Preston's own read
- contested — two live readings, neither chosen
- two live readings disagree — neither chosen yet
- no longer stands
- withdrawn — kept on the record, no longer binds the read
Lenses
Selecting a lens never reloads a list — it dims. Three tiers, from one function: a match stays at full opacity, the thing a match rests on drops to 0.55, everything else to 0.16. A reader can still see what was filtered out and what it sat next to, which a list that removed rows could not show.
The row above is live: choose held, open or withdrawn and the stream dims around it. The counts come from the events themselves — a lens that carried a number nothing produced would be the one thing a receipt must never do.
Receipts
How an understanding formed: the counts, and the load-bearing path through the claims that produced it. Rendered straight off the receipt the server composed and never counted a second time — a surface that re-derives a number is a surface that can disagree with the record it is showing.
- everything accepted is written down
outbox.enqueuebefore the transport- ledger and outbox are one
- the delivery guarantee
The counts above are composed from the five events in the stream, once, at the seam — which is the same thing the server does before this component ever sees them.
How this page knows
A style sweep buys three months; the mechanism is what lasts. There are three parts, and this page is the third.
- One component draws the mark.A fourth brand cannot be introduced by hand, because there is no hand-drawing path left.
- The gate refuses a value spelled anywhere else.
design-system.test.tsfails the build on a colour literal, afont-family, or a bracket size, tracking or radius class outsidepreston-tokens.css. The debt table that let the app get here is now empty, so the rule is simply the rule: not one, anywhere. - This page is measured, and its list is gated.Every value here is read off the element beside it after paint, so it cannot drift.
system-catalog.test.tsholds the catalog equal to the token file in both directions — a token added without a meaning fails, and a meaning whose token was deleted fails too. A system page that could describe a system the app no longer has is worse than no system page.