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

⌘K
empty via search
IDTitle
ISS-1842Tighten selected-state contrast
ISS-1839Ship help drawer reading lane
ISS-1831Map activity to agent timeline
ISS-1820Align edge depth on panels
applicationISS-1842 bound

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.

--brand

selected

muted

theater

Contrast pairs

meridian-a11y — frozen pairs from lib/design/contrast.ts, checked by npm run check:contrast.

  • foregroundbackground
    paper body
  • muted-foregroundbackground
    paper secondary
  • brandbackground
    paper link
  • brand-foregroundbrand
    on-brand fill
  • dock-foregrounddock
    theater body
  • dock-muteddock
    theater secondary
  • branddock
    theater link (lifted brand)

Paper

Soft warm stone — never pure white or pure black. OKLCH only.

background

card

muted

dock

Theme knobs

--brand

--brand-foreground

--brand-muted

--background

--foreground

--radius

Cinematic laws

  1. 1

    productIsSubject

    true

  2. 2

    fluentMaterialWithCursorDensity

    true

  3. 3

    oneIdeaPerFrame

    true

  4. 4

    fullBleedMedia

    true

  5. 5

    noOverlayStickers

    true

  6. 6

    softNeutrals

    true

  7. 7

    oneAccent

    true

  8. 8

    viewport

    svh

  9. 9

    motion

    micro-only

  10. 10

    tileAlternation

    true

  11. 11

    typeWeight

    medium-max

  12. 12

    structuredReading

    true

  13. 13

    oklchOnly

    true

  14. 14

    auditContrast

    true

Material laws

Fluent 2 shape/stroke/elevation expressed as closed Meridian behaviors — absorb, never label.

  1. 1

    controlRadiusSeparateFromLayer

    true

  2. 2

    strokeBeforeShadow

    true

  3. 3

    depthOnlyWhenFloated

    true

  4. 4

    neutralStack

    true

  5. 5

    fourPxGrid

    true

  6. 6

    focusIsStrokeNotFill

    true

  7. 7

    dataIsMono

    true

  8. 8

    aiObjectBound

    true

  9. 9

    selectedBrandWash

    bg-brand/10

fluent2
absorb 9 · reject 3

+ global-alias-tokens

+ neutral-hierarchy

+ brand-sparingly

+ semantic-status

+ control-vs-layer-radius

+ thin-stroke

+ elevation-ramp

+ density-unit

+ rest-hover-selected

cursorApplication
absorb 4 · reject 2

+ quiet-dense-chrome

+ object-bound-ai

+ mono-metadata

+ composer-shaped-proofs

Shape · depth

radii
  • rounded-lg
  • rounded-2xl
  • rounded-3xl
  • rounded-full
depths
  • edge
  • depth-none
  • depth-soft
  • depth-raised

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

32/40 · 14px

  • Selected rowactive
  • Quiet neighbor3h
  • Meta line4h

pad 20 · gap 12 · meta 11px

Marketing

Present · cinema · docs chrome

36/44 · 16px

  • Selected rowactive
  • Quiet neighbor3h
  • Meta line4h

pad 24 · gap 20 · meta 12px

Mobile

Thumb-first · 44px floor

44/52 · 16px

  • Selected rowactive
  • Quiet neighbor3h
  • Meta line4h

pad 16 · gap 12 · meta 12px

Desktop

Keyboard + pointer chrome

28/32 · 13px

  • Selected rowactive
  • Quiet neighbor3h
  • Meta line4h

pad 12 · gap 8 · meta 10px

SurfaceControlRowPad / gapType ui / row / meta
application32px40px20 / 1214 / 13 / 11
marketing36px44px24 / 2016 / 14 / 12
mobile44px52px16 / 1216 / 15 / 12
desktop28px32px12 / 813 / 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

  • --control-h-xs 24px

    Desktop toolbar icons

  • --control-h-sm 28px

    Compact chrome

  • --control-h 32px

    Application default

  • --control-h-md 36px

    Marketing secondary

  • --control-h-lg 40px

    Marketing primary CTA

  • --control-h-touch 44px

    Mobile native targets

Color roles

backgroundforegroundcardpopoverprimarysecondarymutedaccentdestructivewarningsuccessbrandbrand-mutedborderinputringdockdock-mutedsidebar
thinkingsearchreadedit

Banned

Enforced by npm run check:design.

raw-hex-colorarbitrary-color-utilitynon-oklch-colortailwind-shadowdvh-viewportfont-bold-displayscroll-choreographyoverlay-stickers-on-mediapure-whitepure-blackbright-neon-accentcream-terracotta-clichesecond-accentinfluence-brand-labelsfloating-chatbotnested-demo-scrollportsinset-hero-medialow-contrast-on-dockforeground-opacity-cheatdirect-lucide-importdirect-phosphor-importarbitrary-px-heightarbitrary-px-typearbitrary-px-padding

Surfaces

Gallery