Day
A window at daybreak
Body text sits on the canvas at full strength.
Metadata and captions use the muted role.
Cello design system
Every value on this page is read from src/tokens/tokens.json when the site
builds, the same file its CSS is generated from. Two themes, Day and Dusk. Text only ever
sits on a surface. Green means action, earth means ground.
Both themes, side by side. The site follows your system setting until you pick one.
Day
Body text sits on the canvas at full strength.
Metadata and captions use the muted role.
Dusk
Body text sits on the canvas at full strength.
Metadata and captions use the muted role.
Components use roles, never raw colors. Each one carries the line that says when to use it.
--bg-canvas The page. Never inside a component. --bg-raised Panels and card bodies at rest. Don't stack two. --bg-muted Hover fields, code blocks, table stripes. Not for selected or current. --bg-inverse The rare inverted block. Text on it is --text-inverse. --bg-plate An opaque panel carrying text over a scene or image (A1). Never translucent. --text-primary All reading text. --text-muted Metadata, dates, captions, ledes. Never fainter than this for text. --text-inverse Text on --bg-inverse or on --action-primary. --text-on-art Text over scene art (A3, rewrite only): always with the copy scrim or the focus dim under it, and each line passes 4.5:1 (3:1 large) against the 99th-percentile brightest pixel under it. Same in Day and Dusk: the scene, not the theme, is the surface. --text-on-art-muted Labels over art or on the bar (A3): the bar's reading context. Same contrast rule as --text-on-art. --text-accent Section titles, eyebrows, the hero name. Never the only cue for a link. --action-primary Buttons, links, current nav item, scroll bar, selection. Green means action in both themes: signal-500 at Dusk, signal-700 at Day (4.70:1 on paper-100). --focus-ring Keyboard focus on everything: 3px, offset 3px. --accent-ground Grounding shapes: triangles, sills, decorative bars. Earth in both themes. Never text or action. --accent-construction Construction lines and annotations only. Never text or action. --border-strong Specimen frames, buttons, tags. --border-muted Dividers and rules only, never a control's edge. --scrim Behind text on images; behind the menu. The raw ramps the roles are built from. Components never use these directly.
--ink-950 #001a11 Midnight Ink. The Dusk canvas, Day text. Cello family neutral. --paper-100 #f3ede2 Paper 100. The Day canvas. Cello family neutral. --paper-200 #e0e0e0 Starlight. Dusk text. --earth-400 #a89762 Earth. Grounding at Dusk. --earth-500 #887850 --earth-600 #6f613e Deep earth. Grounding in Day. --signal-500 #00bc44 The green sun. Action in Dusk. --signal-700 #007a33 Action in Day. 4.70:1 on Paper 100. --blueprint-500 #42a5ff Construction lines and annotations. Art only: sky, far ridge, snow, forest and ground, pulled from Pacific Northwest photos and flattened like a WPA print. Never text, never action. At Dusk each ink mixes 60% toward Midnight Ink.
June · Meadow
July · Coast fog
August · Dry gold
September · High light
October · Storm sky
National Park
Mt Hood at daybreak
Page and section titles, the hero name. --font-title
Geist Mono
Labels, nav, data and code
Everything else. The site's voice. --font-mono
Geist Sans
Long reading, at about 69 characters a line.
Case-study and blog body text only. --font-read
Switchback sets place names. It arrives with the landscape, where places do.
Cello
Design system
Color roles
One file, every value
Card and h3 titles
Ledes on listings and the homepage.
Cards, the resume and every paragraph outside an article.
Inside an article, the same size reads in Geist Sans.
Buttons and back links
Captions, card descriptions and metadata.
Eyebrows · tags · badges
Rem, named by pixels. Spacing comes from this scale or a layout token.
--space-4 --space-8 --space-12 --space-16 --space-20 --space-24 --space-32 --space-40 --space-48 --space-64 --space-96 --layout-container 1100px--layout-container-wide 1500px--layout-gutter clamp(1rem, 4vw, 2rem)--layout-measure 68ch--layout-measure-read 38em--layout-measure-narrow 46ch--stroke-rule Dividers. --stroke-control Buttons, tags, media. --stroke-frame Cards, specimens. --stroke-bar Heavy bars, mullions.
Parts arrive, lock in and settle, on one curve: cubic-bezier(0.16, 1, 0.3, 1). With reduced
motion, everything finishes at once and no delay is kept, so content never waits.
--ease-out cubic-bezier(0.16, 1, 0.3, 1)--motion-ui 300ms--motion-entry 600ms--motion-stagger 60ms--motion-stagger-dense 30ms--motion-page-wipe 450ms--motion-logo-loop 12s--motion-logo-break 700ms--motion-arrival-stagger 120ms--motion-arrival-overshoot 0.107--motion-focus 550ms--motion-scene-fade 700ms--motion-handoff 400ms--motion-none 0s--motion-reading-active 24s--motion-reading-rest 60s--motion-reading-strength 0.2tokens.json first: the value, the theme pair or the use-when line.pnpm tokens. It writes the CSS this page and the site read.pnpm build fails on a new raw color, size, space or duration, a hand-built keyframe, font or transition, or a token file that's out of date.pnpm ui:shots compares every page pixel for pixel; pnpm motion:table lists every animation for a text diff.