Design System

Accessibility-first. Dark terminal aesthetic. Every decision made for human eyes — aging, tired, dyslexic, low-contrast, keyboard-only. Not an afterthought. The foundation.

WCAG AAA body text 18px minimum Atkinson Hyperlegible Triadic colour harmony
00 — Design philosophy

Philosophy

This system exists for one reason: the manifold workbench is visually intense — Three.js, heat diffusion, real-time 3D geometry. Every page around it must rest the eye, not tax it. High contrast. Wide spacing. Type that doesn't strain. Infrastructure for the mind, not decoration.

01

Legibility over aesthetics

18px body minimum. 1.75 line height. 65ch max line width. Atkinson Hyperlegible — designed for low vision. When in conflict, legibility wins.

02

Contrast is non-negotiable

Body text is WCAG AAA (7:1+) everywhere. No exceptions. The terminal is dark; the text must be bright enough to read it at 3am under bad lighting.

03

Colour signals nothing alone

Every state communicates via shape, border, position, AND colour. Notices have icons. Badges have text. A colourblind user misses nothing.

04

Keyboard is first-class

Tab through every page. Every interactive element has a 3px visible focus ring. No element is mouse-only. No hover-only content.

05

Motion is purposeful

0.18s ease. One property at a time. Hover lifts 1px — barely enough to feel. Motion communicates state change, not personality.

06

Tokens, not magic numbers

Every value is a CSS custom property. No hardcoded hex in component HTML. No arbitrary px outside the 4px scale. Change one token, fix everywhere.

01 — Colour tokens

Colour

Click any swatch to copy its CSS custom property to clipboard. All accent colours tested against --bg-base (#0b0b0f). Contrast ratios shown.

Backgrounds

Aa
--bg-base #0b0b0f Page background
Aa
--bg-surface #13131a Card / panel
Aa
--bg-raised #1c1c26 Hover / elevated
$_
--bg-code #0f0f16 Code / terminal

Text

Aa
--text-primary #eaeaf2 16.8:1 · AAA
Aa
--text-secondary #a8a8c4 7.4:1 · AAA
Aa
--text-dim #66667a 3.5:1 · large/decorative only

Accents — purple primary, triadic harmony

Aa
--accent-cyan #a78bfa · primary purple 7.5:1 · AAA · links, focus, primary
Aa
--accent-green #34d399 · emerald mint 8.3:1 · AAA · code strings, success
Aa
--accent-amber #fbbf24 · warm gold 8.4:1 · AAA · numbers, warnings
Aa
--accent-red #f87171 4.6:1 · AA · errors, seam line
Aa
--accent-violet #7c3aed · deep purple 3.2:1 · visited links only
02 — Token reference

CSS custom property reference

Complete token dump. Click any token to copy. All values set on :root in style.css. Never hardcode values that live here.

Spacing scale — 4px base unit

TokenValuepx
--sp-10.25rem4px
--sp-20.5rem8px
--sp-30.75rem12px
--sp-41rem16px
--sp-51.25rem20px
--sp-61.5rem24px
--sp-82rem32px
--sp-102.5rem40px
--sp-123rem48px
--sp-164rem64px
--sp-205rem80px

Type scale — base 18px

Tokenrempx (at 18px base)Use
--text-xs0.75rem13.5pxCaptions, labels only
--text-sm0.875rem15.75pxMeta, table headers
--text-base1rem18pxBody copy — accessibility floor
--text-lg1.2rem21.6pxCard titles, h4
--text-xl1.5rem27pxh3
--text-2xl2rem36pxh2
--text-3xl2.75rem49.5pxh1

Border radius

TokenValueUse
--radius-sm4pxInline code, focus ring, small chips
--radius8pxInputs, pre blocks, notices, buttons
--radius-lg14pxCards, modals, large panels

Motion

TokenValueUse
--ease0.18s easeAll transitions — one speed, one curve
03 — Typography

Typography

Atkinson Hyperlegible — body and headings. Designed at the Braille Institute for low-vision readers. Each character is shaped to be as distinct as possible. Monospace is reserved for code and terminal output only — never decorative prose.

h1 · --text-3xl
2.75rem · 700
line-height 1.3

The manifold

h2 · --text-2xl
2rem · 700
line-height 1.3

Sym² construction

h3 · --text-xl
1.5rem · 600
line-height 1.3

Heat diffusion loop

h4 · --text-lg
1.2rem · 600
line-height 1.3

Oracle expression scope

body · --text-base
1rem · 400
line-height 1.75
max-width 65ch

Body copy — 18px. The quick brown fox jumps over the lazy dog. Wide line-height gives breathing room between lines — critical for dyslexic readers tracking back to the start of the next line. Letter spacing 0.02em.

label · --text-sm
0.875rem · 400
meta only
Small text — 15.75px. For labels, metadata, captions. Never for body copy.
caption · --text-xs
0.75rem · 400
decorative only
xs / mono — 13.5px — section numbers, token labels, version tags
code · --font-mono
0.875em · 400
letter-spacing 0.06em
manifold.([0,0, 40,0, 20,34])

Prose demo

This is a full prose paragraph demonstrating the reading experience at specification. Letter spacing is 0.02em — enough to separate characters without distorting word shapes. Word spacing 0.04em. Left-aligned only — justified text breaks word-spacing rhythm and is hostile to dyslexia. Bold uses --text-primary to contrast with the secondary body colour. Italic is used sparingly for technical terms. Links underline and use accent-cyan — visible without colour alone.

Lists

  • Unordered — bullet marker uses --accent-cyan
  • Line height matches body: 1.75. Item spacing: 8px (--sp-2)
  • Nested lists should be avoided in most cases — add a sub-heading instead
  1. Ordered list — numbered steps, sequences, ranked items
  2. Same spacing rules as unordered
  3. Use when order matters; unordered otherwise
04 — Code blocks

Code blocks

Terminal output is the DNA of this project. Monospace. Syntax-coloured. Horizontally scrollable. Dark background below page-base so code sits in a pit of shadow.

Shell session

$ manifold.([0,0, 40,0, 40,40, 0,40])
// 4 seed points → Sym²(square), 10 unique pairs
manifold built — 10 vertices, 9 faces
$ oracle = "sin(t*0.04 + d*3.14) * 12"
// oracle active — z-displacement per vertex per frame

PCB console

// connect to Frontier (Claude API channel)
sys.connect('frontier')

// inject seed coordinates and rebuild manifold
manifold.([0,0, 40,0, 20,34])

// oracle: z = originalZ + heat[i] * w
oracle = "sin(t*0.04 + d*Math.PI) * 14"

// reset heat buffer
heat.fill(0)

// ASCII text → seed curve (char code → orbit position)
hello world

Cartridge hook API

// kernelHooks — set this IIFE before window.onload
window.kernelHooks = {
  onInit(scene, camera, controls) {
    // build manifold with 60 pts on a unit circle
    const N = 60;
    const pts = [];
    for (let i = 0; i < N; i++) {
        const a = (i / N) * Math.PI * 2;
        pts.push(Math.cos(a) * 40, Math.sin(a) * 40);
    }
    buildManifold(pts);
  },
  oracle: "sin(t*0.04 + d*Math.PI) * 14",
  background: 0x000000,
  wobble: 0.5
};

Inline code

Use code for identifiers (heat, friends), paths (kernel.js), commands (sys.connect), and values (0x000000). Never for prose emphasis — use bold for that.

Syntax token classes

ClassColourUsed for
.token-cmdaccent-cyanCommands, function calls, shell prompts
.token-stringaccent-greenStrings, string-like values
.token-numberaccent-amberNumbers, booleans, hex colours
.token-commenttext-dim + italicComments, annotations
.token-keyaccent-redKeys, property names, identifiers
.token-valueaccent-violetValues, return data, expressions
05 — Buttons

Buttons

Minimum 44×44px tap target on mobile. Focus ring always visible. Hover lifts 1px — barely perceptible, feedback without flash. Use a for navigation, button for actions.

Variants

ClassWhen to use
.btn-primaryThe one action per page that matters most. Use once per section maximum.
.btn-secondaryImportant but not the CTA. Whitepaper links, secondary confirmation.
.btn-ghostLow priority, supplementary actions. Demo links, "read more."

Sizes

Small btn-sm · inside cards, tables
Default default · most contexts
Large btn-lg · hero CTAs only

States

Rule: disabled buttons get opacity:0.4 and cursor:not-allowed. No separate disabled colour. Don't remove them from focus order — screen readers need to announce why they're disabled.

06 — Badges

Badges

Status labels and state indicators. All-caps with tracking. Always pair with text — colour is never the sole signal. Pill shape (border-radius 99px).

Seam Live Heat Error Frontier Free & Open 0–9 bit depth

Inline usage

Kernel version v1.0 running in Live mode. Oracle status: Warm. Frontier: 401.

In card titles

morph.html Featured

Five shapes lerp continuously. Material colour shifts per shape.

07 — Notices / callouts

Notices

Four semantic states. Border + tinted background + icon — three signals, not one. Distinguishable with colour vision deficiency.

Info

Informational callout. Context, tips, supplemental explanations. Not blocking.

Warning

Something needs attention before proceeding. Frontier channel latency is elevated — oracle expressions may lag behind heat state.

Error

Something went wrong. Frontier returned 401. Check your API key in kernel scope: window._frontier_key.

Success

Manifold built — 1770 vertices, 1740 faces. Heat diffusion active. Friends deployed at vertices 0, 443, 886, 1329, 1749.

Usage

<div class="notice notice-info">
  <span aria-hidden="true">ℹ️</span>
  <div>
    <strong>Title</strong>
    <p>Message body.</p>
  </div>
</div>
08 — Cards

Cards

Contained content units. Surface background lifts from page. Hover brightens border. Accent variant has purple glow for highlighted/featured items.

chord.html

Sym²(S¹) — 60 seed points on a circle, 1770 chords. Cold cyan material. The red seam IS the circle re-embedded at z=0.

Open demo
morph.html Featured

Five curves lerp: circle → star → figure-8 → rose-3 → heart. Material colour shifts with each shape. beforeFrame lerps originalVertices.

Open demo
heat.html

Lava. Inner ring is heat source, outer ring is surface. Material lerps cold → hot in real time based on heat[i] buffer.

Open demo

Usage

<!-- Default card -->
<div class="card">
  <div class="card-title">Title</div>
  <p class="card-body">Body copy.</p>
</div>

<!-- Accent / featured card -->
<div class="card card-accent">...</div>
09 — Forms

Forms & inputs

Inputs follow the same accessibility principles as everything else. Focus ring: 3px cyan glow. Placeholder uses --text-dim (never used as a label substitute). Error state uses red glow. All labels are visible and associated via for/id.

Text input

Enter seed coordinates or a PCB expression.

Monospace input — PCB console style

Evaluated per vertex per frame. Scope: t, d, i, heat.

Error state

Invalid key — Frontier returned 401. Check your key and try again.

Rules

  • Always use a visible <label> — never rely on placeholder alone
  • Error messages sit below the input, in --accent-red, preceded by an icon or the word "Error"
  • Hints sit below the input, in --text-dim
  • Monospace inputs for code/expression entry only
  • Focus ring replaces outline:none with a styled equivalent — never hide focus
10 — Tables

Tables

Left-aligned. Header uppercase with tracking. Row hover tints background. No zebra stripes — bottom borders are enough. Scrollable on narrow viewports.

Command Format Effect
sys.connect sys.connect('frontier') Opens the Frontier (Claude API) channel for natural-language interaction
manifold. manifold.([x,y,...]) Injects coordinates and rebuilds the manifold mesh from scratch
JS expression any expression with =, ., or ( Evaluated against kernel scope — access heat, friends, oracle
Plain text any other string Maps each character's ASCII code to a seed coordinate — rebuilds manifold as an orbital word
11 — Blockquote

Blockquote

4px left border in --accent-cyan. Surface background. Italic prose in --text-primary. For mathematical or domain definitions — rare, authoritative.

The symmetric product Sym²(X) is the space of unordered pairs from X. When X is a smooth curve in the plane, Sym²(X) is a smooth surface in 3D — the midpoint of each pair forms the x,y plane and the distance between the two points rises as z. The diagonal u=v re-embeds X itself at z=0: the seam.

Heat diffusion follows the discrete graph Laplacian: at each frame, every vertex averages its heat with its neighbours, multiplied by decay factor 0.96. Friends inject heat; the surface dissipates it. Equilibrium is a function of injection rate, graph topology, and oracle amplitude.

12 — Border radius

Border radius

Three values. Consistent use signals component type — sm for inline, base for interactive, lg for containers.

--radius-sm
4px
code, chips, focus
--radius
8px
inputs, buttons,
pre, notices
--radius-lg
14px
cards, modals
99px
badges, pills only
13 — Spacing scale

Spacing

4px base unit. All layout spacing is a named token. Never hardcode pixel values outside this scale — every value drifts, every token propagates.

sp-1
4px
sp-2
8px
sp-3
12px
sp-4
16px
sp-6
24px
sp-8
32px
sp-10
40px
sp-12
48px
sp-16
64px
sp-20
80px
14 — Motion

Motion & transitions

One timing token: --ease: 0.18s ease. Fast enough to not feel sluggish. Short enough to not feel animated. Hover the cards below.

dot lifts on hover
transform 0.18s ease
card lifts + border
all 0.18s ease
background fade
background 0.18s ease

Rules

  • Use var(--ease) — never write raw timing values
  • Transition one property at a time where possible — transition: color var(--ease) not all
  • Exception: buttons use transition: all var(--ease) because they change multiple properties
  • No transition: all on layout elements — it causes reflow on resize
  • No animation on elements with prefers-reduced-motion — add a media query if you add keyframe animations
/* respect reduced motion */
@media (prefers-reduced-motion: reduce) {
  * { transition: none !important; animation: none !important; }
}
15 — Accessibility

Accessibility

Tab through this entire page. Every interactive element — links, buttons, inputs — should have a visible 3px cyan focus ring. Nothing is mouse-only.

Focus ring demo

Scrollable region

// scrollable regions get tabindex="0" and role="region" // with an aria-label so screen readers announce them // this region is keyboard-accessible via arrow keys // once focused by Tab manifold built — 1770 vertices, 1740 faces heat buffer: Float32Array(1770) friends: 5 agents, life 1000 ticks oracle: "sin(t*0.04 + d*Math.PI) * 14" seam: LineSegments, 60 points, accent-red material adjacency: graph built from mesh topology, O(V+E) tick 0 → tick 1 → tick 2 → ...

Full checklist

  • 18px minimum body text — not 14px, not 16px
  • 1.75 line height for prose — give lines room to breathe
  • 65ch max line width — prevent eye-tracking fatigue on wide monitors
  • Left-aligned text only — justified text distorts word spacing, hostile to dyslexia
  • WCAG AAA (7:1+) for all body text — not just AA
  • WCAG AA (4.5:1) minimum for all UI, large text, icons
  • Visible focus rings — 3px solid --accent-cyan, always
  • Never colour as sole signal — notices use border + tint + icon
  • Semantic HTML — headings in order, landmarks, aria-label on nav/region
  • Atkinson Hyperlegible — designed for low-vision, dyslexia-aware characters
  • No justified text — ever, anywhere, any breakpoint
  • Letter spacing 0.02em — reduces visual crowding without distorting words
  • Word spacing 0.04em — signals word boundary more clearly
  • Monospace letter spacing 0.06em — wider, since characters are less distinct
  • Scrollable regions get tabindex="0" and role="region" with label
  • Images get descriptive alt text or aria-hidden="true" if decorative
  • Interactive icons get aria-label — never rely on icon shape alone