Scratch Proto
A dark, monospace design language built around an exposed-wireframe feel:
a dot-grid substrate, faint scanlines, registration corner-marks, amber caution accents,
and build-stamp metadata. It reads like an internal instrument that was never meant to ship —
and looks proud of it. This page is the canonical reference, reconciled from the
scratch and simple-llm-ui repos.
Color
5 surfaces · 4 text · amber primary + green/red statusSurfaces step in near-black increments — almost no chroma. The whole system rides on three
semantic colors, each with one job: --accent (amber) is primary action / caution,
--signal (green) is success / live, --danger (red) is
error / destructive. Amber is locked — keeping green and red unambiguous as status. Everything else
is neutral so those three carry all the meaning.
Typography
2 families · monospace-firstSpace Grotesk for display (headings, names),
JetBrains Mono for everything else. The monospace body is core to the
identity — it makes every screen feel like console output. Two signature prefixes carry hierarchy without extra weight:
> for primary titles, // for captions and comments.
Spacing & Radius
neither repo had a formal scaleThe source repos used ad-hoc values (10/12/14/16px, 1.15rem…). This formalizes a
4-based scale — existing values snap to the nearest step on export. Radius is
0 by default: the system is hard-edged. The only rounded elements are true pills and status
dots (--r-pill).
Components reference semantic spacing tokens — not raw px — so padding and gaps are tuned from one place.
| --pad-control-y | buttons · inputs · tabs (vertical) | 8px |
| --pad-control-x | buttons · inputs · tabs (horizontal) | 12px |
| --pad-card | cards · list items · modal rows | 18px |
| --gap-stack | vertical gap inside stacks | 8px |
Motion
click a curve to previewMotion is restrained and quick. One curve only — --ease-out — is
used for every transition; nothing eases any other way. UI transitions sit at 0.09–0.11s, overlays
at 0.19s. The only motion that runs unprompted is the looping “live system” pulse (status LED,
streaming caret), which signals work in flight.
Signature Patterns
the load-bearing motifsThese are what make it Scratch Proto and not just another dark theme. Use them deliberately — they carry the "exposed instrument" personality.
A 24px radial dot-grid is fixed to the viewport behind everything; a faint 2px scanline rides on top. Both reconciled on (scratch's variant).
Motion rule: a dot pulses only while work is actively happening —
running or queued. Every other state is a static dot; the pulse is a signal that
something is in flight (e.g. the auto-reload poller checking for a new build), never ambient decoration.
Components
lifted verbatim from the reposThe first true component — <scratch-button>, a shadow-DOM
custom element. Structure and styles are sealed inside; only the design tokens reach in. There's no
.btn class to misuse, so a button literally cannot be assembled wrong. Variants are an attribute.
Buttons are form-associated:
type="submit" submits the owning <form> (native validation included). No
type, no submit — a plain button never touches a form, so existing wiring is untouched.
type covers the
single-line modes — text (default) · password · number · search · email · url — with min /
max / step passed through for number use. multiline ignores it.
Component <scratch-select> — light-DOM <option> children are
the options source, mirrored into a sealed control and kept live by a MutationObserver (edit the list any time;
the current value survives while it still names an option). Form-associated; fires composed
input/change. Option text renders verbatim — no uppercase, so model ids read as-is.
Component <scratch-toggle> — form-associated checkbox riding a hidden native
<input type="checkbox">, so keyboard and a11y come for free. The box is hard-edged; checked fills
an inset amber square (LED-adjacent, never rounded). The label is slotted — click anywhere to flip.
Component <scratch-progress> — a thin instrument bar. value /
max drive the fill; state picks the semantic color; indeterminate sweeps a
segment on the system curve. The motion rule holds: under prefers-reduced-motion the sweep is replaced
by a static dim 40% fill — no motion, same meaning. Updates are cheap (a style.width mutation), so
feeding it every frame is fine. Height via --progress-height (6px).
Component <scratch-tabs> with <scratch-tab label="…"> children —
each tab's label and panel live in one element, so they can't desync. The connected-underline strip is
generated and sealed; selected sets the default; switching fires change.
The second sealed component: <scratch-preview> holds a color, sample, or
image placeholder in its body (via color or a slot) above a caption (label /
sub). The entire Color section is built from instances of it.
One component — <scratch-nav> with <scratch-nav-item> children —
powers both this overlay specimen and this page's own left rail. Items auto-number and carry the
connected indicator. desc is optional: omit it and you get the shorter rail-style button (see
the rail at left).
Reconciliation
scratch vs simple-llm-ui · resolvedThe two repos drifted. Per your call, the canon adopts scratch's richer variant, fixes outright bugs, and adds the scale neither had.
| Decision | scratch | simple-llm-ui | Canon | Verdict |
|---|---|---|---|---|
| Scanline overlay | on | dropped | on | kept |
| Card corner-marks | brackets | plain box | brackets | kept |
| Prototype badge | badge-proto | plain .badge | both, scoped | kept |
| --signal-glow / --warning | defined | missing | restored | fixed |
| --ease-out (sole curve) | defined | missing | restored; --ease-bounce dropped | fixed |
| var(--muted) typo | — | broken ref | → --text-muted | fixed |
| grid-dot opacity | 0.08 | 0.08 / 0.06* | 0.08 | fixed |
| Spacing scale | ad-hoc | ad-hoc | 4-based, 8 steps | added |
| Radius tokens | 4 / 6px raw | 4 / 6px raw | 0 — square; pill for dots | added |
| Type scale | implicit | implicit | 8 named steps | added |
* nav.css fallback drifted to 0.06; canon locks 0.08 everywhere.