Theme
See the system.
Twist the knobs — brand, radius, paper — and watch the workbench reskin. Strict OKLCH, frozen in lib/design.
Skill loop
Theme is step one — every Meridian skill has a live proof on this site.
Theme playground
App settings energy on a marketing page — presets write scoped CSS variables; the workbench stays interactive.
Live knobs
Reskin the workbench
Closed presets only — brand · radius · paper. Layout shells and motion stay laws. Export below feeds MCP apply_prefs (6 CSS vars).
Ink teal · Compact · Warm paper
| ID | Title | Meta | |
|---|---|---|---|
| ISS-1842 | Tighten selected-state contrast | 2h | |
| ISS-1839 | Ship help drawer reading lane | 48m | |
| ISS-1831 | Map activity to agent timeline | — | |
| ISS-1820 | Align edge depth on panels | 1d |
Install these prefs
Copy into your project, or ask the contract MCP to apply_prefs. Not a freeform theme editor — unknown ids fail closed.
MCP call
apply_prefs({ brand: "teal", radius: "compact", paper: "warm", surface: "application" })contract.prefs.json
{
"contractId": "meridian",
"brand": "teal",
"radius": "compact",
"paper": "warm",
"surface": "application"
}
app/contract-prefs.css
/* contract-prefs — generated from closed preset ids. Do not invent OKLCH here.
* contractId=meridian brand=teal radius=compact paper=warm
* Re-apply via MCP apply_prefs. Layout/motion are laws, not prefs.
*/
:root {
color-scheme: light;
--brand: oklch(0.42 0.09 200);
--brand-foreground: oklch(0.97 0.01 85);
--brand-muted: oklch(0.93 0.03 200);
--accent: oklch(0.93 0.02 200);
--primary: oklch(0.42 0.09 200);
--primary-foreground: oklch(0.97 0.01 85);
--ring: oklch(0.42 0.09 200);
--success: oklch(0.42 0.09 200);
--chart-1: oklch(0.42 0.09 200);
--sidebar-primary: oklch(0.42 0.09 200);
--sidebar-ring: oklch(0.42 0.09 200);
--background: oklch(0.965 0.01 72);
--foreground: oklch(0.28 0.02 55);
--card: oklch(0.98 0.008 72);
--card-foreground: oklch(0.28 0.02 55);
--popover: oklch(0.98 0.008 72);
--popover-foreground: oklch(0.28 0.02 55);
--radius: 0.375rem;
}
Accent
One deep ink-teal. On theater stages, brand lifts for AA contrast.
Contrast pairs
meridian-a11y — frozen pairs from lib/design/contrast.ts, checked by npm run check:contrast.
Paper
Soft warm stone — never pure white or pure black. OKLCH only.
Theme knobs
Cinematic laws
- 1
productIsSubject
true
- 2
fluentMaterialWithCursorDensity
true
- 3
oneIdeaPerFrame
true
- 4
fullBleedMedia
true
- 5
noOverlayStickers
true
- 6
softNeutrals
true
- 7
oneAccent
true
- 8
viewport
svh
- 9
motion
micro-only
- 10
tileAlternation
true
- 11
typeWeight
medium-max
- 12
structuredReading
true
- 13
oklchOnly
true
- 14
auditContrast
true
Material laws
Fluent 2 shape/stroke/elevation expressed as closed Meridian behaviors — absorb, never label.
- 1
controlRadiusSeparateFromLayer
true
- 2
strokeBeforeShadow
true
- 3
depthOnlyWhenFloated
true
- 4
neutralStack
true
- 5
fourPxGrid
true
- 6
focusIsStrokeNotFill
true
- 7
dataIsMono
true
- 8
aiObjectBound
true
- 9
selectedBrandWash
bg-brand/10
Shape · depth
Shell rhythm
Same chrome across every app shell — spacing, control height, row height, and type remap through data-surface. No off-scale pixel inventing.
Application
Operate data-rich work
- Selected row
- Quiet neighbor
- Meta line
Marketing
Present · cinema · docs chrome
- Selected row
- Quiet neighbor
- Meta line
Mobile
Thumb-first · 44px floor
- Selected row
- Quiet neighbor
- Meta line
Desktop
Keyboard + pointer chrome
- Selected row
- Quiet neighbor
- Meta line
| Surface | Control | Row | Pad / gap | Type ui / row / meta |
|---|---|---|---|---|
| application | 32px | 40px | 20 / 12 | 14 / 13 / 11 |
| marketing | 36px | 44px | 24 / 20 | 16 / 14 / 12 |
| mobile | 44px | 52px | 16 / 12 | 16 / 15 / 12 |
| desktop | 28px | 32px | 12 / 8 | 13 / 12 / 10 |
Focus & keyboard
UX pillar — focus is ring/stroke weight, not brand fill alone. Every control needs a keyboard path (keyboardParity).
Keyboard parity
Focus & shortcuts
0/4
Tab reaches every control
Move focus with Tab / Shift+Tab
Focus shows ring, not fill alone
focus-visible:ring-ring on Button + Input
Rows activate with Enter / Space
Select the demo row below by keyboard
Escape clears the field
Type in the input, then press Escape
Control scale
Color roles
Banned
Enforced by npm run check:design.