The GRAIN design system

One interface. A person and an AI operate it through the same controls.

A human click and an AI decision become the same intent, flow through one door, and come back as visible edits. The AI's presence is shown, never hidden: grain is the signature of a machine at work.

Draft the Thursday plan. the AI types it in grain, and it stays grain (provenance persists) ·
How it works

Every surface on this page is one of GRAIN's own components. Open the Catalog tab, then hover anything (or tap it on a touch screen) to reveal its entry.

Grade as signal

One rule reads instantly and everywhere: grain type means the AI is acting or a value is in transit; clean type means a human wrote it or it has committed. Same words, two typefaces, no badges to learn.

a personDraft the Thursday plan.

the AIDraft the Thursday plan.

Two hands, one thread

YouPlan my afternoon around the review.
GRAINOn it, drafting a plan and triaging today's items.

You (clean) and the AI (grain) in one conversation: the AI's line stays grain; provenance never evaporates into looking human.

Your optimistic action settles

  • Archive last week's notesarchived

Archiving is your action: grain while in-transit, then clean once it commits. AI speech, by contrast, stays grain; only your committed data reads clean.

grain: the AI, in transit clean: a human, committed
One surface, both operators desk offline: live demo unavailable

A real GRAIN composition, operated through the one door. Ask it something yourself, or watch the AI act: both post the same Intent to /intent and render back over SSE. There is no privileged AI → DOM path; the same page powers this demo.

GRAINAsk me to plan something, or watch me act.
  • Draft the Q3 plan active
  • Review the design thread done
⌘K
Interaction timeline every crossing of the one door: human and AI, recorded identically
    How it worksmanifest

    The whole system is one closed vocabulary of verbs and surfaces (contract.ts). A human click and an AI decision are the same Intent; both flow through one door and return as RenderOps pushed over SSE. There is no privileged AI → DOM back channel.

    • One vocabulary. Verbs and surfaces live in a single registry, referenced, never re-typed as strings.
    • A manifest the AI reads. Each screen publishes a drift-proof, machine-readable map of what's operable at /ai/manifest, harvested from the components, so the AI is shown the page, not told about it.
    • Self-documenting. Every component auto-appears in a live component catalog: the design system browses itself.
    • Auditable by design. Every interaction, human or AI, is one source-tagged Intent through one server-side door, so a full human + AI interaction log is a server-side drop-in.
    • Two door transports, one contract. On the live server the door is POST /intent; on a static host this page opts into the client-side door: the same vocabulary and the same ops, run in your browser with no backend. The client door is deliberately fenced: everything it loads is client-safe by contract, static-style modules only, no secrets or tokens, nothing that needs a server. Anything that does need one (real data, a real model) stays behind the server door and is simply absent here.
    • No build step. Server-rendered hypermedia on BATCH: Bun, TypeScript, CSS, htmx. Ship the source.
    Themes

    Every theme is a pure token override: one signature accent hue, no component edits (the topbar ◆ toggle cycles these site-wide too). Pick one:

    Sourdough

    Warm monochrome e-ink: the default, hueless.

    Baguette

    Clean and crisp: near-white light, near-black dark, a soft-blue accent.

    Brioche

    Warm cream and honey-gold: the cozy one.

    Where it sits in the stack
    PANTRY · the app that composes the four PROOF CRUMB MILL GRAIN BATCH

    Built on BATCH: no build step. This site is itself built with GRAIN, on the default theme; a product re-skins it by overriding tokens. The "watch the AI act" demo above runs through the real door, and the Themes tab re-skins the whole site live.