Tokens
Identity is --brand-*. Status and chrome are --sys-*.
Specimens live under Foundations. This page is architecture, install, and retune.
Components use utilities from bindings.css. A look retune fills --brand-* (primary,
secondary, type, radius). System owns chrome and status (danger, success, warning, info).
A prefab may override a status slot. Chrome (the neutral room — surfaces, ink, borders)
stays achromatic until you pick a paper temperature or a warm prefab.
Primitives → Brand (identity) → Bindings
off-limits primary, secondary, type, radius components use these
→ System (chrome + status + cat) → Bindings
room, danger, success, --sys-cat-*--cat-* are primitives (categorical data hues). --sys-cat-* adds --chrome-chroma-tag
(the flat tag scalar, not the l-aware chrome curve) and keeps the hue, except gray, which
follows the room. A brand retune does not touch them.
Want to try a retune? Open the brand studio.
Chrome temperature
Chrome is the neutral room: --sys-bg, --sys-surface*, --sys-fg*, --sys-border*.
Brand color (--brand-primary, support, danger, etc.) sits on top. Paper temperature
only tints that chrome — it does not recolor the brand.
Base, warm, and cool
The dry neutral ramp (--neutral-*) is achromatic (chroma 0). Temperature presets
take each chrome role's lightness from that ramp and paint chroma + hue on top.
| Paper | Knobs a look sets | What it does |
|---|---|---|
| Gray (base) | none (omit) | No tint. Surfaces read as neutral gray. |
| Warm | --chrome-chroma: var(--warm-chroma) · --chrome-hue: var(--warm-hue) | Taupe / Vellum warmth. Fitted to Figma neutrals/gray. |
| Cool | --chrome-chroma: var(--cool-chroma) · --chrome-hue: var(--cool-hue) | Blue-gray coolness. Fitted to slate on the same L ramp. |
Washi and ICS are warm prefabs: data-theme="washi" / data-theme="ics" set those warm
knobs for you. The sidebar's paper picker in these docs is host-only: it toggles
data-chrome on the shell and app/global.css maps that to the knobs above. The installed
kit responds to [data-theme], not data-chrome — that picker axis lives in the docs host.
The calc (both temperatures)
Warm and cool share one curve shape; only hue and peak chroma differ. l is the
lightness of the underlying --chrome-* role (from the neutral ramp):
chroma(l) = base + peak × (1 − l) × l
sys-role = oklch(from var(--chrome-role) l chroma(l) hue)| Hue | Base | Peak | Tag chroma* | |
|---|---|---|---|---|
| Warm | 72° | 0.0042 | 0.05 | 0.012 |
| Cool | 230° | 0.0042 | 0.048 | 0.012 |
* --warm-chroma-tag / --cool-chroma-tag — scalar average for --sys-cat-* (categorical
chips). Chrome surfaces use the l-aware --warm-chroma / --cool-chroma calc instead.
Knobs (the --warm-* / --cool-* curves and their tag scalars) live in
registry/dry/tokens/system.css. system.css also paints every --sys-* surface from
--chrome-chroma / --chrome-hue, so a look only has to set those two knobs to tint the
room — a warm prefab does this for you, or set them on your own [data-theme].
Try it
The sidebar paper picker sets data-chrome on the whole docs shell. The demo below
is an isolated compare (ink primary, room only shifts). Ramp strips show a reference
palette vs the calc on the dry L ramp.
Figma Vellum reference vs warm chrome on the dry achromatic L ramp.
Slate blue-gray reference vs cool chrome on the same L ramp.
—
Body ink on canvas. Muted sits lower. Subtle is the faintest.
—
—
Body ink on canvas. Muted sits lower. Subtle is the faintest.
—
—
Body ink on canvas. Muted sits lower. Subtle is the faintest.
—
—
Body ink on canvas. Muted sits lower. Subtle is the faintest.
—
—
Body ink on canvas. Muted sits lower. Subtle is the faintest.
—
—
Body ink on canvas. Muted sits lower. Subtle is the faintest.
—
Install
shadcn add @gesso/tokensImport the token files from the CSS entry, after Tailwind, so @theme inline resolves.
@import "tailwindcss";
@import "./styles/dry/primitives.css";
@import "./styles/dry/brand.css";
@import "./styles/dry/system.css";
@import "./styles/dry/bindings.css";Chrome is achromatic gray by default. Warm/cool paper comes from a prefab
(shadcn add @gesso/washi or @gesso/ics), or set the knobs on your own look:
[data-theme="acme"] { --chrome-chroma: var(--warm-chroma); --chrome-hue: var(--warm-hue); }.
brand.css wraps itself in @layer gesso.brand. Leave your remaps unlayered so they
beat that invert even if they are imported first.
Retune (most consumers)
Stay on the kit ramps. Override color only, or type only, or both. Surfaces, ink, and
lines stay with system. Leave the remap unlayered. Do not put --brand-* in
gesso.brand. If you skip layers entirely, brand overrides must come after brand.css.
[data-theme="acme"] {
--brand-primary: var(--blue-600);
--brand-primary-fg: var(--neutral-50);
}Then <html data-theme="acme">. Dark is .dark on the same tree and inverts chrome
in system.css. The dry default primary and secondary are ink, so they invert too. A
chromatic --brand-primary or --brand-secondary on [data-theme] does not.
Brand tokens (point each at a primitive, not another --brand-*):
| Group | Tokens |
|---|---|
| Primary | --brand-primary, --brand-primary-fg, --brand-primary-subtle |
| Secondary | --brand-secondary, --brand-secondary-fg, --brand-secondary-subtle |
| Type | --brand-font-ui, --brand-font-body, --brand-font-display |
| Radius | --brand-radius (boxes), --brand-radius-control (buttons) |
| Stroke | --brand-font-weight-ui → derived --sys-stroke → border-stroke, font-ui |
Each hue has a solid, a foreground, and a subtle tint (the old soft / emphasis pair).
--brand-primary-subtle binds direct (→ bg-primary-subtle / border-primary-subtle);
--brand-secondary-subtle seats in chrome via --sys-soft (→ bg-secondary-subtle), so its
fill picks up paper temperature. The solid second hue is consumed as bg-support (shadcn owns
secondary). Filled chip: bg-secondary-subtle + text-fg; outline: border-primary-subtle text-primary.
System status (point at a primitive; a prefab may override the slot). Ink is the 600 step (warning 500); muted fills are 50, and 950 in dark:
| Group | Tokens |
|---|---|
| Danger | --sys-danger, --sys-danger-fg, --sys-danger-muted |
| Success | --sys-success, --sys-success-fg, --sys-success-muted |
| Warning | --sys-warning, --sys-warning-fg, --sys-warning-muted |
| Info | --sys-info, --sys-info-fg, --sys-info-muted |
shadcn's bg-secondary / bg-accent are aliases for the fill wash (bg-fill).
The second brand hue is bg-support / text-support-fg. Kit chrome names and
the alias table live on Color. Switch Washi or ICS in
the sidebar to see blossom or orange land on support.
font-sans is UI (buttons, inputs, the shell). font-body is prose. font-display is
titles. Bindings read --brand-font-* directly. font-mono is --sys-font-mono only;
there is no brand mono. Sizes and leading are a named scale (text-display,
text-heading-1–6, text-body-*, text-caption). Looks retune faces, not metrics. See
Typography. Color roles:
Color.
Radius: --radius-scale-* is the catalog brand picks from. --brand-radius is the box pick.
The unsized token (rounded) is medium, so rounded-md is the same value. rounded-lg
derives from it (+ 4px). --brand-radius-control is buttons only
(rounded-control). Chips pin the catalog step --radius-scale-md in component markup so they
do not inherit pill controls or ICS box radius. rounded-full stays a circle. Dry and
Washi pick --radius-scale-md (8px) for box and control. ICS picks --radius-scale-full on control
(pills) and --radius-scale-xl on boxes. Switch them in the sidebar. Bindings read
--brand-radius* directly. Specimens: Radius.
Stroke: set --brand-font-weight-ui only — border-stroke derives from it
(400 → 1px, 600 → 2px, clamped to the --stroke-1 / --stroke-2 catalog cap).
Bindings expose border-stroke and @utility font-ui.
Surfaces stack; shadows use one non-inverting ink (shadow-sm / md / lg). See
Elevation. Overlay motion is fade on poppers, scale
(not transform) on centered dialogs, translate on sheets. See
Motion.
These docs load Washi's faces as the host default: Hanken Grotesk on font-sans,
Newsreader on .prose, Fraunces on headings (SOFT 4). An Atlas retune fills the
same roles with Sora on font-sans and font-body (running UI and copy are one
face) and Outfit on font-display. Atlas's own --font-sans token is Outfit, but
that slot is headings there; do not copy the label.
Prefab (Washi)
Washi is warm chrome (self-contained in washi.css) plus wave/blossom on --brand-*,
vermilion on --sys-danger, the three faces, and 8px corners. Load the font files in
the host; the prefab only names them.
shadcn add @gesso/washi@import "./styles/dry/themes/washi.css";Unlayered, same as a retune: wave and blossom survive .dark even if this import sits before brand.css.
<html data-theme="washi">Dark is .dark with data-theme="washi": lightness inverts, warmth and wave stay.
Prefab (ICS)
ICS is warm chrome plus Atlas v2 type plus violet and orange. The prefab defines
--ics-violet-* and --ics-orange-* as primitives (off-limits, not bg-violet-300).
Brand picks 600. Sora is font-sans and font-body. Outfit is font-display.
Controls are pills (--brand-radius-control: var(--radius-scale-full)). Boxes are --radius-scale-xl.
It keeps dry status; only the warm paper knobs and the chromatic-soft bypass touch
--sys-*. Load Outfit and Sora in the host; the prefab only names the faces.
shadcn add @gesso/ics@import "./styles/dry/themes/ics.css";<html data-theme="ics">Dark is .dark on the same tree with data-theme="ics": chrome lightness inverts;
brand remaps to lighter violet primary, orange-600 soft, and violet-300 emphasis
(see themes/ics.css).