ONE Design System

6 editable tokens. 3 depth levels. 1 card. 1 input. Every component derives from these — pick a token, the whole system responds. Spec lives in design.md.

I · Vocabulary

Six tokens, three depth levels, one polish scale. Everything below is derived from these — nothing here is a one-off.

The 6 editable tokens

Click any swatch to recolor live. Picks persist to localStorage.

Surface tokens (background · foreground · font) flip with mode — picks apply to the dark mode you're viewing. Brand tokens apply to both.

  • background L1 surface — cards, sidebar, popovers
    #1a1a1a
  • foreground L2 surface — card body, inputs, code
    #212121
  • font Body text — derives border · muted · faint
    #ffffff
  • primary Main CTA — also focus ring (both modes)
    #1d3a66
  • secondary Supporting actions (both modes)
    #3d4451
  • tertiary Accent · success-tinted (both modes)
    #324e32
Invariants — destructive success white black + derived: on-{primary,secondary,tertiary} · border · muted · page

Three levels of depth

Page (L0) → card (L1) → content (L2). No 4th level. Sidebar = L1. Inputs = L2.

L0 page · --color-page
L1 card · --color-background
L2 content · --color-foreground

Polish constants

Baked into components. Not editable. Snap to scale or polish breaks.

Radius

--radius-sm6px · chips
--radius-md10px · buttons, inputs
--radius-lg16px · cards

Shadow

--shadow-cardcards, popovers
--shadow-popmenus, modals

Spacing

4 · 8 · 12 · 16 · 20 · 24 · 32 — every padding/gap lands here.

II · Themes

A theme is six colours per mode and nothing else. The border, the page, the muted surface and the focus ring are all derived from those six — which is why a palette can invert the card/page ladder and every surface still lands correctly. Press one and the page you are reading repaints.

Named palettes

Defined once in src/lib/themes.ts. They are the same object a stored brand is — six triples per mode — so they go through the same apply() below and the picker does not know which kind it is wearing. Your pick persists to localStorage; saving a workspace brand clears it, because a workspace's palette outranks a personal one.

Paper · ships by default

Warm stock and espresso ink. The card sits BELOW the page, so a panel reads as a sheet laid on a desk rather than a box floating over grey.

  • primary 23 77% 9%
  • secondary 28 13% 32%
  • tertiary 33 35% 49%
  • background 37 23% 94%
  • foreground 36 24% 92%
  • font 22 52% 4%

Charcoal

The original neutral. A white card on a grey page — the conventional ladder, kept selectable so the pre-Paper look is one click away rather than a git revert.

  • primary 216 55% 25%
  • secondary 219 14% 28%
  • tertiary 105 22% 25%
  • background 0 0% 93%
  • foreground 0 0% 100%
  • font 0 0% 13%

III · Brands

The same vocabulary, worn by two workspaces. Press Wear this brand and every surface on this page repaints — that is the claim at the top of the page, run rather than asserted.

Two workspaces, one vocabulary

The 6 tokens, per workspace, read from <slug>/site.md in R2 at request time — the same object Layout.astro reads to paint every branded page. Nothing here is transcribed, so a value shown is a value served.

one · one.ie

12 values stored · 6 light · 6 dark

  • primary #101828
  • secondary #5b6b84
  • tertiary #1f6feb
  • background #eef1f5
  • foreground #ffffff
  • font #0b111c

ehc · ehc.one.ie

12 values stored · 6 light · 6 dark

  • primary hsl(190 78% 24%)
  • secondary hsl(268 34% 34%)
  • tertiary hsl(152 58% 27%)
  • background hsl(38 42% 95%)
  • foreground hsl(0 0% 100%)
  • font hsl(222 30% 14%)

The workspace context decides, not the path. ehc.one.ie wears EHC's palette because its workspace context is ehc; one.ie/ehc wears ONE's, because that route is on the one host. Both are correct, and which one a given surface should wear is a product decision rather than a defect.

IV · Components

What the vocabulary builds. One card, one input, two icon registries — each reading the tokens above, never a colour of its own.

Card anatomy

Header · body · footer share one card surface

L1
Body rectangle L2 — where data lives
  • Strength0.84
  • Resistance0.12
  • Cycles128
Updated 2m ago

Form controls

All inputs share one shape · L2 surface · 10px radius

forms
Shown on your public profile
Enter a valid email address
Auto-saves on blur

Buttons & states

6 variants. on-* labels auto-contrast.

buttons
All buttons: 10px radius · 120ms ease · ring focus

Icons

One source: lucide-react. 1.5px stroke · round caps · token-driven color. Wrap with <Icon> or <IconBadge>.

IconBadge — tones

Used in feature grids, list items, profile rows.

primary
tertiary
secondary
neutral

Sizes — sm · md · lg

Icon — sizes

Inline icons. Inherit color from parent via currentColor.

sm · 14 md · 16 lg · 20 xl · 24

In context

EHC icon set — 38 names

6 families, each derived from a surface that already existed and had no icon — nothing was invented to round out a pack. Real Lucide components, so they inherit the 1.5px stroke and paint with currentColor: no hex can enter the set. Source: lib/ehc-icons.ts.

The confidentiality mark — load-bearing, not decoration

Three states told apart by silhouette only. Fill and hue both die at 16px under a six-token palette, so neither may carry the meaning.

ehc-locked Only the people in my meeting can see this
ehc-open Anyone with my plan can see this
ehc-interpreted Someone wrote this down for me — these are not my own words

ehc-interpreted is not a lock. It is a provenance claim that this came through an adult, it sits beside the lock, and it must never fuse with it — it is the glyph that stops “the CYP agrees” being written. And the lock never means you are denied access; it means your circle is small, which is a promise to the child rather than a refusal to the reader.

confidentiality — 3

ehc-interpreted ehc-locked ehc-open

voice capture modes — 8

voice-aac voice-audio voice-drawing voice-helper voice-photo voice-showing voice-video voice-writing

cohorts — 7

cohort-complex-communication cohort-complex-medical cohort-early-years cohort-looked-after cohort-post-16 cohort-primary cohort-secondary

lifecycle stages — 8

stage-assess stage-decide stage-deliver stage-draft stage-finalise stage-request stage-review stage-transition

domains — 3

domain-care domain-education domain-health

pod roles — 9

role-chairman role-cyp-voice role-doctor role-keyworker role-local-authority role-parents role-senco role-social-worker role-therapist

1 of 7 EHC document templates are built — the minutes, at lib/ehc/minutes.ts. The rest are specs in text/ehc-templates-plan.md and text/ehc-deck-ui.md; lib/ehc/templates.ts is the registry that counts them.

A cohort icon names the duty owed, never a body. No wheelchair, no ear, no signing hands, no child figure — complex-medical is the s.42 obligation, complex-communication a speech mark and not a mouth, looked-after the corporate parent. The test is one line: if the glyph names a person's body it is wrong; if it names the duty, it is right.

Card Vocabulary

13 card types. Every surface in the product derives from one of these shapes.

Refunds Bot

Live

Handle refund requests

refunds-bot

1200 ms

Live

Agent deployed

3 checks passed

What would you like to do?

System prompt

Has name
Has prompt
>20 words

Monthly plan

9.99USD

Brand Palette

Stripe PaymentsAccept credit cards

Support Bot

$5/mo

ui:chat:send

depth 3
outcomemark

Compare

Before$240/mo
After$120/mo

Get started

1/3

No agents yet

Create your first agent to get started

Motion identity

A field of paths. A pulse takes one. Unused recede. The field holds. Live tokens.