SPEC SHEET
THE SYSTEM
Shadowless, square-cornered. Monospace for structure, Geist for reading, one gray stepped by alpha, 1px rules instead of elevation, 150ms for everything — and on top of the gray, four hues that name a state and never decorate one. This page is rendered by the live system it documents.
SURFACES
There is no elevation ladder, because there is no elevation. Depth is a 1px hairline or a muted fill, and nothing floats. Every surface is defined twice — once light, once dark — and the toggle in the footer has three states, because “follow the OS” is one of them.
- --ground#ffffff · #0a0a0a · the page
- --panel#ffffff · #171717 · cards, popovers
- --panel-2#f5f5f5 · #262626 · hover fill, code
INK
There is still no accent colour, in the sense that matters: no hue is the brand, and no hue is spent on wanting to be looked at. The brand colour is the foreground itself, used inverted, and that inversion is the loudest thing the system can do — so a screen gets one.
- --ink#0a0a0a · #fafafa · headings, links, bold
- --muted#5c5c5c · #b0b0b0 · secondary text, nav at rest
- --faint#737373 · #8c8c8c · counters, timestamps
- --edge#e5e5e5 · #fff / 10% · every hairline
SIGNAL
Four hues, one job each. Green is live, blue is shipped, yellow is provisional, red is over — and a fifth would mean a distinction nobody has actually drawn. Every one of them clears AA as text on both the page and the hover fill, in both themes, which is the test for whether a colour is a colour or a decoration: if it cannot carry a word, it should not be carrying a meaning.
- --green#0f7a3d · #3ecf7e · live — building, currents, the decrypt
- --blue#1b58c8 · #7fb2ff · shipped — and anything that launches outward
- --yellow#8a6200 · #e3b341 · provisional — experiment, trialing
- --red#c81e28 · #ff6b6e · over — dead, and every destructive action
Where they show up: the mode light on the homepage and the pulse on the currents strip, the unresolved characters of the wordmark as it decrypts, every status badge, the hairline across a shipped card, the cause-of-death panel, and the phosphor scanlines in CRT mode. Nowhere else.
THE ALPHA LADDER
Instead of inventing gray-400/500/600, one colour steps down in opacity and each step has a fixed job. Learn the ladder and the whole system follows.
| STEP | JOB |
|---|---|
| --ink | headings, bold, links, hover target |
| --ink / 90% | long-form body copy |
| --muted | secondary body, nav at rest, card descriptions |
| --faint | counters, timestamps, decorative arrows — never load-bearing |
Status is where the ladder and the signal palette meet. The mono label says BUILDING or DEAD in words, so the hue is a second channel and never the only one — that is what makes it safe to add rather than something a reader has to decode. paused keeps the gray on purpose: it is the one status defined by the absence of activity, and a colour would be claiming more than is true.
TYPE
Geist Sans carries the prose.
Geist Mono is the structural voice: labels, data, annotations.
UPPERCASE LIVES ONLY IN MONO · 0.15EM
Tracking runs inverse to size, aggressively: display type is tightened to -0.025em, and 10px mono labels are opened to +0.12em. That is what makes small mono read as a system label rather than as fine print. Six of the ten most-used sizes sit below 1rem — this is a small-type system with one very large exception.
The tonal split,
the second line.
MOTION
No motion library — CSS, the platform's View Transitions, and one number-rolling primitive. One duration and one curve carry almost everything:
| PARAMETER | VALUE | NOTE |
|---|---|---|
| everything | 150ms | colour, border, opacity — the default and the rule |
| --ease-standard | cubic-bezier(0.4, 0, 0.2, 1) | the only curve |
| entrances | 500ms | reveal, 10px rise, stagger 60ms, ≤8 stages |
| hover | colour only | no transform, no weight change, no lift |
| reduced motion | everything stops | opacity cues stay, movement goes |
COMPONENTS
Structure and behavior live in @zckr/ui TSX; every visual decision lives in one style file as zk-* classes. Restyle the system by editing one CSS file.
ZK-CARD
Square, hairline, no fill at rest. Hover promotes the fill to --panel-2 and nothing else moves. In a grid, prefer .rule-grid: the 1px gap draws the borders, so rules never double.
INPUT
PATTERNS
- lines divide, not whitespace — sections get border-t, list rows border-b, the page is a ruled ledger
- card grids draw their borders with the gap: .rule-grid is gap-px over --edge, so 1px rules never double
- the tonal split carries every display heading — bold ink, then light and muted, and that is the whole expressive budget
- corner ticks frame unboxed regions — registration marks, not borders
- reveal entrances rise 10px over 500ms, staggered 60ms via --stage, base state visible for no-JS
- page navigations crossfade content through native View Transitions; the chrome holds still, and lab filters morph each surviving card to its new slot
- long notes get a scroll-driven reading progress bar — zero JavaScript where supported
BANNED
A design system is mostly the things it refuses. These are load-bearing refusals:
- shadows — depth is a hairline or a muted fill, and there is no third option
- radius — square by default; the search field and the keycap are the two exceptions
- an accent colour — hue names a state here; it never decorates one, and it never brands anything
- colour as the only carrier of meaning — the label still says DEAD, and the hue is the second channel, never the first
- a fifth hue — green live, blue shipped, yellow provisional, red over; anything else is a decision nobody made
- border beams, ambient gradients, glow — motion and light without information
- bento grids — this is a ruled ledger, not a product launch
- glassmorphism — hairlines carry structure here, not blur
- skeleton shimmer — pages are static; there is nothing to pretend to load
- custom cursors, scroll hijacking, sound