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.
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.
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.
Colour signals nothing alone
Every state communicates via shape, border, position, AND colour. Notices have icons. Badges have text. A colourblind user misses nothing.
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.
Motion is purposeful
0.18s ease. One property at a time. Hover lifts 1px — barely enough to feel. Motion communicates state change, not personality.
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.
Colour
Click any swatch to copy its CSS custom property to clipboard. All accent colours tested against --bg-base (#0b0b0f). Contrast ratios shown.
Backgrounds
Text
Accents — purple primary, triadic harmony
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
| Token | Value | px |
|---|---|---|
--sp-1 | 0.25rem | 4px |
--sp-2 | 0.5rem | 8px |
--sp-3 | 0.75rem | 12px |
--sp-4 | 1rem | 16px |
--sp-5 | 1.25rem | 20px |
--sp-6 | 1.5rem | 24px |
--sp-8 | 2rem | 32px |
--sp-10 | 2.5rem | 40px |
--sp-12 | 3rem | 48px |
--sp-16 | 4rem | 64px |
--sp-20 | 5rem | 80px |
Type scale — base 18px
| Token | rem | px (at 18px base) | Use |
|---|---|---|---|
--text-xs | 0.75rem | 13.5px | Captions, labels only |
--text-sm | 0.875rem | 15.75px | Meta, table headers |
--text-base | 1rem | 18px | Body copy — accessibility floor |
--text-lg | 1.2rem | 21.6px | Card titles, h4 |
--text-xl | 1.5rem | 27px | h3 |
--text-2xl | 2rem | 36px | h2 |
--text-3xl | 2.75rem | 49.5px | h1 |
Border radius
| Token | Value | Use |
|---|---|---|
--radius-sm | 4px | Inline code, focus ring, small chips |
--radius | 8px | Inputs, pre blocks, notices, buttons |
--radius-lg | 14px | Cards, modals, large panels |
Motion
| Token | Value | Use |
|---|---|---|
--ease | 0.18s ease | All transitions — one speed, one curve |
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.
2.75rem · 700
line-height 1.3
The manifold
2rem · 700
line-height 1.3
Sym² construction
1.5rem · 600
line-height 1.3
Heat diffusion loop
1.2rem · 600
line-height 1.3
Oracle expression scope
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.
0.875rem · 400
meta only
0.75rem · 400
decorative only
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
- Ordered list — numbered steps, sequences, ranked items
- Same spacing rules as unordered
- Use when order matters; unordered otherwise
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
| Class | Colour | Used for |
|---|---|---|
.token-cmd | accent-cyan | Commands, function calls, shell prompts |
.token-string | accent-green | Strings, string-like values |
.token-number | accent-amber | Numbers, booleans, hex colours |
.token-comment | text-dim + italic | Comments, annotations |
.token-key | accent-red | Keys, property names, identifiers |
.token-value | accent-violet | Values, return data, expressions |
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
| Class | When to use |
|---|---|
.btn-primary | The one action per page that matters most. Use once per section maximum. |
.btn-secondary | Important but not the CTA. Whitepaper links, secondary confirmation. |
.btn-ghost | Low priority, supplementary actions. Demo links, "read more." |
Sizes
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.
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).
Inline usage
Kernel version v1.0 running in Live mode. Oracle status: Warm. Frontier: 401.
In card titles
Five shapes lerp continuously. Material colour shifts per shape.
Notices
Four semantic states. Border + tinted background + icon — three signals, not one. Distinguishable with colour vision deficiency.
Informational callout. Context, tips, supplemental explanations. Not blocking.
Something needs attention before proceeding. Frontier channel latency is elevated — oracle expressions may lag behind heat state.
Something went wrong. Frontier returned 401. Check your API key in kernel scope: window._frontier_key.
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>
Cards
Contained content units. Surface background lifts from page. Hover brightens border. Accent variant has purple glow for highlighted/featured items.
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 demoFive curves lerp: circle → star → figure-8 → rose-3 → heart. Material colour shifts with each shape. beforeFrame lerps originalVertices.
Lava. Inner ring is heat source, outer ring is surface. Material lerps cold → hot in real time based on heat[i] buffer.
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>
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:nonewith a styled equivalent — never hide focus
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 |
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.
Border radius
Three values. Consistent use signals component type — sm for inline, base for interactive, lg for containers.
4px
code, chips, focus
8px
inputs, buttons,
pre, notices
14px
cards, modals
badges, pills only
Spacing
4px base unit. All layout spacing is a named token. Never hardcode pixel values outside this scale — every value drifts, every token propagates.
4px
8px
12px
16px
24px
32px
40px
48px
64px
80px
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.
transform 0.18s ease
all 0.18s ease
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)notall - Exception: buttons use
transition: all var(--ease)because they change multiple properties - No
transition: allon 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; }
}
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-labelon 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"androle="region"with label - Images get descriptive
alttext oraria-hidden="true"if decorative - Interactive icons get
aria-label— never rely on icon shape alone