puchkode spent four colours on four jobs: amber for the accent, cyan for links, green and red for state. phosphor spends one. The greys are not grey — they are the accent, starved of chroma, so nothing on the page is off-family.
phosphor
A design system with one hue. Everything a second colour would normally carry — a link, the current place, progress, weight — is carried by inversion and by how full a run of block characters is.
- ok hues: 1 (+1 for failure)
- ok faces: 1 — IBM Plex Mono
- ok radius: 0, no exceptions
- ok moving parts: 1 — the cursor
cat one-hue.txt
State has to go somewhere. It goes into density: a run of ░▒▓█ that always encodes a number you could otherwise read. Never decoration — if you cannot say what value it shows, it does not ship.
cat tokens/colour.oklch
Every ratio below was measured by converting the OKLCH value to sRGB and computing WCAG contrast, not estimated. worst is the lowest the token reaches on any surface it is painted on; the floor is 4.5:1 for text and 3:1 for a control border.
| token | oklch | hex | role | on ground | worst | |
|---|---|---|---|---|---|---|
| ph-0 | 0.148 0.008 265 | #090b0e | ground — the glass | — | — | n/a |
| ph-1 | 0.200 0.006 70 | #181613 | surface — code blocks, kbd | — | — | n/a |
| ph-2 | 0.250 0.010 70 | #25211d | raised — inline code, hovered rows | — | — | n/a |
| ph-3 | 0.340 0.014 70 | #3d3730 | line — decorative dashes only | 1.67:1 | 1.36:1 | n/a |
| ph-4 | 0.545 0.028 70 | #7b6e5f | line-strong — frame corners, input underline | 3.96:1 | 3.22:1 | ok |
| ph-5 | 0.660 0.035 72 | #a08f7b | muted — dates, metadata, captions | 6.31:1 | 5.13:1 | ok |
| ph-6 | 0.850 0.032 78 | #daccb7 | ink — body text | 12.45:1 | 10.11:1 | ok |
| ph-7 | 0.950 0.022 85 | #f5eede | ink-bright — headings, strong | 17.04:1 | 13.84:1 | ok |
| ph-8 | 0.810 0.145 72 | #fab048 | amber — accent, prompt, cursor, focus | 10.68:1 | 8.67:1 | ok |
| ph-fail | 0.660 0.170 27 | #e86156 | the only off-hue — failure states | 5.87:1 | 4.76:1 | ok |
A link has no colour of its own. It is body ink with a dotted underline, and hover inverts it into a selected terminal cell — amber field, ground text, measuring 10.68:1 either way round. This link demonstrates it.
cat tokens/type.css
One face doing every job, display included. The mono here is the concept — a character grid — not a shorthand for “technical”, which is why puchkode's second display face was retired rather than restyled.
- t-display · clamp(2.5rem, 8vw, 6rem) / 0.92 / -0.03em / 700
- phosphor
- t-h2 · 1.375rem / 2rem / 700 · 22 / 32
- ## What survives the trip
- t-h3 · 1.125rem / 1.5rem / 700 · 18 / 24
- ### Keeping your classes
- t-body · 1rem / 1.5rem / 400 · 16 / 24
- It implements the structured clone algorithm, and that algorithm has opinions.
- t-code · 0.875rem / 1.375rem / 400 · 14 / 22
- const copy = structuredClone(original);
- t-small · 0.8125rem / 1.25rem / 400 · 13 / 20
- #javascript #browser
- t-label · 0.75rem / 1rem / 500 / 0.08em · 12 / 16
- NOTE
cat tokens/grid.css
One line is 24px, one column is 1ch. Every space token is a fraction or multiple of the line — if something does not land on it, it is wrong.
space-16px — quarter line — code-head paddingspace-212px — half line — gaps inside controlsspace-324px — one line — the base unitspace-448px — two lines — before a sectionspace-596px — four lines — between page regionsbarshadewavels ~/brand
The k is a hole in the path, so whatever is behind the mark shows through it.
ls ~/structure
One sliced SVG: crosses on the corners, dashed runs between them. The corners sit on the edges by construction, so nothing can drift.
The same frame with its dashes stepped up to line-strong. One box per page earns this.
A dash of 0.6ch every 1ch — the character pitch, not an arbitrary dotted border.
ls ~/content
- structuredClone is not a deep copy of everything7 min
- AbortController beyond fetch6 min
- CSS anchor positioning, finally5 min
- Hunting memory leaks with heap snapshots8 min
What survives the trip
Anything that crosses a thread boundary as plain data comes through intact, including the cases JSON mangles. Press Ctrl + F and search for => when a clone throws.
Keeping your classes
- Functions and DOM nodes throw
- Class instances come back as plain objects
- Getters are evaluated once and stored as values
class Money { static from({ cents, currency }) { return new Money(cents, currency); }}// no hue left for syntax — weight and luminance carry itconst price = Money.from(structuredClone(new Money(1999, "EUR"));Structured clone copies data, not behaviour. If it has methods, it needs a constructor on the other side.
the rule I taped to my monitor
| value | JSON round-trip | structuredClone | support |
|---|---|---|---|
| Date | string | Date | 100% |
| Map / Set | {} | Map / Set | 97% |
| class instance | plain object | plain object | 0% |
| function | dropped | DataCloneError | 0% |
ls ~/actions
Weight is the tag's real share of the archive. A tag cloud usually says this with font size; the grid is fixed here, so density says it instead.
ls ~/edge-cases
The states a specimen page usually skips. Everything below is the real component under hostile input, not a mock-up of one.
grep: no matches for webassembly —
puchkode drew this rail as a literal run of twenty ">" characters in a pseudo-element, which meant a quote longer than twenty lines simply lost its rail partway down with nothing to indicate anything was wrong. This paragraph is deliberately long enough to cross that boundary on a narrow viewport, and the rail beside it is now a masked chevron tiled at the line height, so it continues for as many lines as the quote runs to. The colour still comes from the amber token rather than being baked into the artwork, because the mask carries the shape and the background-color carries the hue. Keep reading and the rail keeps going.
the reason the rail is a mask now
01negativeover 1NaNls ~/feedback
- ok now: writing about AbortSignal
- waiting reading: the Temporal spec
- failed build: 1 test failing on main