Siliconwhiz · Design System v2.0

SUBSTRATE

The layer everything is built on. Obsidian canvas, one hot signal, and motion that only ever means something — rendered by the running theme, not screenshotted from it.

01 / ARCHITECTURE

Three layers. No shortcuts.

A component may never reference a primitive. It reads a component token, which reads a semantic token, which reads a primitive. That indirection is what lets you re-theme, ship a light mode, or change the accent in one line without auditing 400 files.

Layer 1 · Primitive

Raw values

Named for what they are. No meaning attached. Never used directly in a component.

/* the palette */
--volt-400: #C8FF3D;
--void-900: #0A0B0D;
--slate-200:#BDC2CB;
Layer 2 · Semantic

Purpose aliases

Named for what they do. This is the layer that themes — the Customizer writes here. Swap these, and everything downstream follows.

/* the meaning */
--accent: var(--volt-400);
--bg-base:var(--void-900);
--fg-muted:var(--slate-200);
Layer 3 · Component

Local contracts

Named for where they apply. Lets one component deviate without forking the system.

/* the contract */
--btn-primary-bg: var(--accent);
--btn-primary-fg: var(--accent-fg);
--card-bg: var(--bg-raised);
// ✗ WRONG — a component reaching past two layers
.button { background: #C8FF3D; }
.button { background: var(--volt-400); }

// ✓ RIGHT — every hop is explicit
.button { background: var(--btn-primary-bg); }
//                    └─ --accent ─── --volt-400 ─── #C8FF3D
02 / COLOUR

One signal. One atmosphere. Everything else is obsidian.

Roughly 92% of every screen is neutral. Voltage appears only where something is live, active, or actionable. If the accent is on more than about 6% of the pixels, the page has stopped meaning anything.

The obsidian ladder

Four elevations, not two. Pure black is banned: it smears on OLED, and it leaves no room to go darker for recessed surfaces like inputs and code blocks.

bg-void
#06070A
Recessed — inputs, code, wells. Below the page.
— surface —
bg-base
#0A0B0D
The page itself. The default plane.
— surface —
bg-raised
#111318
Cards, panels. One step toward the viewer.
— surface —
bg-overlay
#191C22
Modals, popovers, sticky nav on scroll.
— surface —

Text ramp

fg
#F4F5F7
Headlines, primary text.
15.64:1 · AAA
fg-muted
#BDC2CB
Body copy, paragraph text.
9.54:1 · AAA
fg-subtle
#8C93A0
Secondary, card body.
5.52:1 · AA
fg-dim
#7E8592
The floor. Labels, captions, metadata. Nothing may be dimmer.
4.60:1 · AA

Voltage — the signal

These read live from the theme. The accent is currently #C8FF3D — change it in Customize → Substrate and this row moves with it.

accent
#C8FF3D
Primary CTA, live state, active nav, the running signal.
16.71:1 · on bg-base
accent-hover
#B3E82F
Hover.
— state —
accent-press
#8FBF20
Pressed.
— state —
accent-fg
#06070A
Ink on voltage. Chosen by contrast, so a custom accent never lands on unreadable ink.
17.10:1

Plasma and status

ambient
#4A2BFF
Glow and gradient only. Ambient light fields, hero atmosphere, the far edge of a blur. Never text. Never a fill. Never a border.
2.75:1 · FAILS AA — by design
success
#3DE0E0
Passed · verified · reconciled. Cyan, not green — the accent is a yellow-green, so a green “passed” would read as the same family at chip size.
10.51:1 · AAA
warn
#FFB443
Degraded, pending, rate-limited.
9.64:1 · AAA
danger
#FF4D4D
Failure, destructive. 77° from the accent — never confusable with “live”.
5.22:1 · AA
border-strong
#666D7A
The only border allowed to define a control — inputs, ghost buttons. Hairlines cannot; they fail 1.4.11.
3.56:1 · non-text ✓

✓ Do

  • Let plasma live at ≤22% alpha, behind everything, pointer-events:none.
  • Use voltage for exactly one primary action per viewport.
  • Reach for bg-void when a surface should feel recessed — inputs, code, terminals.

✗ Don't

  • Set plasma as text or a button fill. It is 2.75:1. It will fail an audit and it will look cheap.
  • Add a fifth accent. Two colours carry meaning here; a third destroys both.
  • Use border-hair to outline an input. It is decorative — it cannot define a control.
03 / SIGNATURE

The Substrate

Not a node graph. Not a logo constellation. Not two boxes with an arrow between them. Those depict plumbing, and nobody wakes up wanting plumbing.

The device is a field of particles in noise that resolves into an ordered lattice — and holds. It is the whole company in one gesture: we take an ambiguous problem and return a structured system. The cursor is an attractor, so the field visibly responds. Move your mouse across it.

State Visual Meaning
NoiseParticles scattered, dim, drifting on a random walkThe brief. Ambiguity. What the client arrives with.
ResolvingParticles ease toward lattice anchors; edges begin to draw between neighboursThe work. expo.out, staggered — order emerging, not snapping.
ResolvedLattice holds, edges at full voltage, entropy reads 0.00The system. Running. This is the terminal state.
PerturbedCursor pushes particles off-anchor; they spring backProduction. It takes a hit and returns to true.

Reduced motion: the canvas renders the resolved lattice statically on first paint, entropy already at zero. The argument survives with the motion removed entirely, which is the only honest test of whether an animation earns its place. No JS: the canvas is skipped and the section collapses to its caption — no empty box, no stalled “RESOLVING” label.

04 / TYPE

Three faces, three jobs

Archivo carries every display line — it has genuine mechanical character where Inter has none. Inter does all the reading. JetBrains Mono owns every number, label, identifier and line of code.

Scale
Shipping it is the job.
Section headline — Archivo 900 · 85%
Card title — 20 / 800
Body is Inter at 16–17px, line-height 1.65, capped at 66 characters. It is invisible on purpose. If a reader notices the body typeface, the body typeface is wrong.
const latency = 412ms · p95 1.2s · eval 94.1%
Label · JetBrains Mono · 10.5 / .18em
Role Face Size / LH Tracking Rule
HeroArchivo 900 · 85%clamp(40 → 78) / .94−.022emMax 6 words. Tight enough to look engineered.
H2Archivo 900 · 85%clamp(30 → 50) / 1.02−.024emOne per section.
H3 / cardArchivo 800 · 85%17.5–22 / 1.3−.016em
BodyInter 40016–17 / 1.650Cap at 66ch. Never Archivo.
Data / codeJetBrains Mono 50012.5–15−.01emtabular-nums always. Every metric, every ID, every latency.
LabelJetBrains Mono 60010.5–11+.18em, UPPEREyebrows, section indices, chip text.
05 / COMPONENTS

Built, not described

Button

A11y: 48px height clears the 44pt floor. The focus ring is voltage, visible on every surface in the system — which is not true of most dark themes. The arrow glyph is aria-hidden. Disabled anchors use aria-disabled, never pointer-events alone.

Card and status

RUNNING

Agent systems

Retrieval, tool use, evals, fallbacks, and the boring production scaffolding that decides whether it survives contact with real users.

SHIPPED

Product engineering

The application the model lives inside. Auth, data, billing, admin — the thing your customers actually touch.

DEGRADED

Status variant

Chips carry a dot, a word, and a colour — three redundant signals. Colour is never the only one.

Hover: the border lifts, the card rises 3px, shadow-2 lands, and a top-edge specular fades in — a 1px gradient hairline that reads as light catching an edge. It is the single detail that makes an obsidian card feel like a physical object rather than a div.

Input

Field
Use a work email — we look up your company before we reply.

Recessed to bg-void, one step below the page — an input should feel cut into the surface, not stuck on top of it. The border is border-strong at 3.56:1; the hairline would fail WCAG 1.4.11.

06 / MOTION

One curve. Everything.

cubic-bezier(.16, 1, .3, 1) — expo.out. It leaves fast and lands soft. Every entrance, hover, reveal and transition in the product uses it, which is why the whole thing feels like one object instead of forty.

Token Duration Curve Applies to
--t-1140msexpo.outHover, focus, chip, press. Feedback must land inside 100ms perceptually.
--t-2240msexpo.outCard lift, border transition, state change.
--t-3420msexpo.outScroll reveal — opacity + 20px rise. Fires once, never on re-entry.
--t-4680msexpo.outHero char stagger, circuit advance. The long ones.
Stagger22ms/char, capped at 420msBeyond the cap the last character feels laggy.

✓ Do

  • Animate transform and opacity. Nothing else. Ever.
  • Make every animation depict a cause and effect — a state resolving, a signal moving, a system responding.
  • Keep animations interruptible. A tap during a transition cancels it immediately.

✗ Don't

  • Animate width, height, top or left. That is a reflow every frame.
  • Pin more than one section. Scroll-jacking fights native scroll and is miserable on mobile.
  • Ship anything whose meaning is lost under prefers-reduced-motion. If the end state does not carry the argument, the animation was decoration.

SILICONWHIZ · SUBSTRATE v1.0.0
Rendered live by the theme it documents.