Working document · editable

Pocket UI — System Brief

Defining the goal for a system that lets an AI agent (and a human) build on-brand pages without guessing. Last updated: Feedback pass — smaller type, chip radius, no caps, adaptive markers.
confirmed answered by you open still to decide
Operating principle — never guess
Every value, token, and decision must trace to a concrete source (the source system code or the design file) or explicit, stated logic. If something can’t be found or is ambiguous, stop and ask — don’t invent. Check in before structural decisions.

North-star goal

Build a source-derived, fully tokenized design system with a parametric theming engine at its core. Extract the source’s design language (the design file + code) into an exhaustive token foundation, then let an agent (or human) re-parametrize it from minimal input: give one color anchor (e.g. a 900) and it derives the full coherent scale; swap the type families (e.g. General Sans / Geist Mono) and it re-applies them across components; pick a radius scale (sm/md/lg with defined ranges) and everything updates, all previewable on real components. Build the foundation (visual tokens) first, then structure / layout and behavior / states, working toward duplicating + re-theming components to compare variations and, ultimately, one-shotting on-brand pages. Delivered as paired artifacts: an agent-facing spec (.md) and a mirror human-readable doc, backed by a component library solid enough to one-shot things.

Direction change (Round 2): base moves back to the source system (extract their system in detail); the previously-built transit system is set aside for this effort. The crux is a parametric theming engine, not a fixed look. This goal statement is my synthesis — correct it freely.

Round 1 — framing

Primary user confirmed

Who is the primary “builder” this system serves?
Primarily AI agents, with humans able to use it too. The system ships in two mirrored forms: an agent-facing file (e.g. .md) and the same content in a human-readable / usable form (e.g. HTML).

What the system physically is confirmed

Which artifacts should exist?
  • Machine-readable spec — tokens, scales, rules, constraints an agent can load and obey.
  • Living docs — browsable guidelines + examples (the human-readable mirror).
  • Component library + recipes — coded components plus composition recipes, solid enough to one-shot things.
A standalone “agent context pack” wasn’t called out separately; the agent-facing .md spec plays that role.

What gets generated on the fly confirmed

What must be generatable, fully grounded in the system?
  • Components — net-new, derived from the same tokens/rules so they feel native.
  • Layouts / sections — reusable blocks that snap together on-brand.
  • Full pages — when asked, assembled end to end.
  • Motion / interaction — treated as polish applied along the way via comprehensive guidelines, not a separate generation target.

First target surface confirmed

What does the system have to produce first?
Marketing-style sites first, with the system flexible enough to expand into building products (app UIs) later.

Round 2 — direction

Base + core idea confirmed

What personality should the sites have?
Go back to the source system as the base — it has an extensive design-file system and code; extract a design system from it in as much detail as possible. The point isn’t a fixed look, it’s a parametric, themeable foundation you can re-drive from a single input.

Capabilities the system must support confirmed

Concrete things you want to be able to ask for:

Build order confirmed

Foundation vs blocks; what’s exhaustive first?
Design system / base first. Within “never guess,” start with the visual foundation (tokens), then build out structure / layout and behavior / states over time. Content / voice is not an early focus.

One-shot bar confirmed

What’s the target?
Ideally a one-line prompt → full on-brand page; a brief → strong draft to refine is an acceptable bar too.

Round 3 — engine specifics

Sources of truth confirmed

Where do we extract the source’s system from?
Both, with you unsure of the best extraction path (I’ll figure it out):
  • Codethe source repopackages/design-system (on develop).
  • Design file — the source design file (link in your message). Use it for variables, components, and page structure.

Color derivation confirmed approach TBC

From one anchor, how is the full scale derived?
You leaned toward matching the source’s own scale curve, re-pinned to your anchor, and asked me to recommend.
My recommendation: reuse the shape of the source’s ramp (its relative lightness + chroma per step) but recompute in OKLCH pinned to your anchor’s hue. That keeps output on-brand to the source system, perceptually even, and reliable for generating both light and dark from a single token. To be confirmed.

Token scope confirmed

Which categories must the foundation cover?
All of them: Color · Typography · Space / size / radius / border · Elevation / motion / misc (z-index, breakpoints).

Preview model confirmed

How do you see a theme change after asking for one?
Non-destructive: the system generates a new page/variant with the new theme and leaves the original blueprint intact. Same idea as “duplicate + re-theme” — you compare, the source is never edited in place.

Round 4 — build approach

Components confirmed

Wrap the source system, or build our own?
source-informed but our own generic components — not literal the source UI package wrappers. They must be generic enough to build a comprehensive marketing site and, since the product won’t differ much from the site, a product screen too. Every component is token-only (no hardcoded values) so theming flows through.

Seeding modes confirmed

What does a single color anchor drive?
Build all three modes so you can see and decide what to keep:
  • Brand/accent only — neutrals + semantics stay the source system.
  • Brand + tinted neutrals — grays lean toward the anchor.
  • Everything from anchor — brand, neutrals, semantics all derived.
Anchor accepts a hex or a scale index (e.g. a -900).

Dark mode confirmed

First-class now, or light-first?
Light first, but the foundation stays dark-ready — tokens + derivation built so dark can switch on without rework.

My recommendations your call to override


Proposed build plan


Phase 0 — extraction findings grounded in code

Source: the source repopackages/design-system/ui-preset/src (theme/tokens/{colors,typography,effects}.ts, theme/extension/theme.ts, constants.ts). These are the source’s real, exported token values — not inferred. Design file not yet read.

Architecture: semantic-token-first, light + dark

The source system exposes role-based CSS variables, each defined for light and dark. Components never touch raw colors — only roles. Categories found:

Underlying palette

The semantic values resolve to Tailwind’s default palette: zinc = neutrals, blue = interactive/brand (light blue-500, dark blue-400), rose = danger, and emerald / amber / violet / orange for tags. There is no separate 50–950 ramp file — semantics reference resolved palette values directly. So the seed→ramp engine needs us to (re)generate numeric ramps and re-map semantics onto them.

Typography

Two families: Inter (sans) + Roboto Mono (mono); weights 400 / 500 only. Named text styles by family: h1–h4-webs (marketing, up to 4rem), h1–h3-core (app), h1–h4-docs, body txt-* (xsmall→xlarge, -plus=500), txt-compact-* (tight 1.25rem leading), code-* (mono).

Elevation / shadows

Rich, layered, light + dark — the source’s signature. Tokens: elevation-card-rest/hover, elevation-flyout / tooltip / modal / commandbar / code-block, plus buttons-*, borders-* (base/error/focus/interactive), details-switch-*. These double as borders+focus rings, not just drop shadows.

⚠ Gap: radius / spacing / breakpoints are NOT custom

The preset only customizes colors and boxShadow. Spacing, border-radius, breakpoints, and the font-size scale fall back to Tailwind defaults (radius: 2 / 4 / 6 / 8 / 12 / 16 / 24px / full; spacing 4px base; screens 640/768/1024/1280/1536).

Implication: your parametric radius scale (sm / md / lg) is a new layer we design — the source system has no equivalent. Needs your confirmation of the exact scales (see decisions below).


Decisions — resolved answered

Design-file cross-check done

The design file is the same source UI file and its variables match the code (spot-checked: foregrounds/fg-base = #18181b = code’s --fg-base; font/family/body = Inter). The design file organizes tokens as collections: foregrounds/, backgrounds/, borders/, font/family/, font/weight/, Labels/ (type styles). The Dev-Mode tool reads variables per node (leaf nodes work; whole pages don’t).

Conclusion → sources strategy: code is the authoritative token source (it’s the exported output of these design-file variables); the Tailwind palette supplies the exact numeric ramps; the design file is reserved for component anatomy + page layouts (read later via get_design_context when we build components). Nothing is guessed. Minor item to reconcile: the design file stores line-height as a unitless multiplier, code stores rem — normalize when we build the type scale.


Phase 1 proposal — foundation confirm before build

Token set → full the source system port decided

Port the entire the source system token set verbatim (light + dark): all bg / fg / border / button / tag / contrast / alpha colors, the full typography styles, and every elevation shadow. Nothing trimmed — maximum fidelity. We build our additions and theming engine on top of it.

Additions on top decided — add both

Radius model (both: scale + modes) locked

Token scale (px, grounded in Tailwind’s radii): xs 4 · sm 6 · md 8 · lg 12 · xl 16 · 2xl 24 · full.

Semantic radii components reference: radius-control (inputs/buttons) · radius-card · radius-popover · radius-pill.

Global modes remap the semantic radii (your final values):

rolesharpdefaultround
control268
card / panel4812
popover / menu4812
pillfullfullfull

Phase 1 — built done + verified

Token source of truth — tokens/

Generated CSS — app/foundation.css

scripts/build-tokens.ts (run via npx tsx) emits the foundation: :root light, .dark / [data-theme="dark"], [data-radius="sharp|round"], and the typography utilities wired to --font-* (so a family swap re-flows every text style). Single source → generated CSS, no hand-editing.

Live reference — /foundation now exhaustive

Renders every ported token with its name + live-resolved value: Backgrounds (20), Foregrounds (9), Borders (8), Buttons (12), Tags (30), Contrast (9), Alpha (2) = 90 colors; plus Additions (14), Shadows (23), Radius, Typography (35). Alpha/transparent tokens render over a checker so transparency reads. light/dark + sharp/default/round toggles re-theme the whole tree via scoped CSS variables (verified: dark → bg-base #212124, accent → #3b82f6; sharp → control 2px) — the non-destructive mechanism the engine will drive.

Primitives captured ramps · spacing · icons done

tokens/primitives/colors.ts — the full Tailwind v3 palette (22 hues × 11 steps = 242), the exact atoms behind the source's semantics (verified zinc-900 #18181b, blue-500 #3b82f6, rose-600 #e11d48). tokens/primitives/spacing.ts — Tailwind v3's 35-step spacing scale. Both emitted as CSS vars (--color-<hue>-<step>, --space-<n>).

Iconscomponents/icons/: our own set (per your choice), drawn on the source's grid (15px · 1.5 stroke · round caps · currentColor), grown as needed. 21 to start (check, x, chevrons, arrows, search, trash, pencil, copy, info, alert, more, external-link, user, settings…). All on /foundation.

Plan reorder (your priority)

You asked for, in order: 1) exhaustive reference done2) capture missing primitives (color ramps 50–950, explicit spacing scale, icons) → 3) components (Phase 3)4) theming engine (Phase 2). Components now come before the engine.


Phase 3 — components started

First batch (site-first, token-only)

Live at /ds: Button (accent / primary / neutral / ghost / danger · sm/md/lg · icons · states), Badge (6 the source system tag colors), Card (outline + elevated). New namespace components/ds/ (the parked v1 ui/ and transit v2/ are left untouched). Every value reads a CSS variable — verified the whole set re-themes across light/dark + sharp/default/round with no component code change.

Styling basis: token-referencing classes (bg-[var(--bg-accent)], rounded-[var(--radius-control)]) — no hardcoded values. A shorter utility-alias layer (Tailwind @theme) can be added later without touching components.

Next in Phase 3

Remaining site-first pieces: section scaffolding — nav / header, hero, feature grid, footer, CTA — composed from these atoms. Then more controls (Input, Select…) and the theming engine (Phase 2 / your #4).


Theming engine — refined model your key idea

Single neutral seed drives all chrome

One dark neutral seed (e.g. a 950 from slate/gray/zinc/neutral/stone, or any custom dark color) generates a full neutral ramp. All chrome — surfaces, text, borders, buttons — derives only from that ramp. Accent colors (blue/red/green) are reserved for markers (chips, badges, status), never chrome. Change the one seed → the whole site's personality changes (cool / warm / pure), while functional markers stay constant.

Generator — built + verified /engine

Browser-native: each step is oklch(from var(--neutral-seed) <L> c h) — one seed variable drives the whole ramp, no JS color math, no dependency. The lightness curve (L per step) is grounded in zinc's actual OKLCH so contrast stays proven; the seed supplies hue + chroma. Verified: slate→cool, stone→warm, zinc→pure, custom teal→teal-gray, all live from one color.

Boundary calls decided

Chrome rewire — done + verified /ds

Each chrome token was mapped to a ramp step by matching its OKLCH lightness to the zinc reference (53 chrome tokens neutralized, 37 markers kept literal — tokens/chrome.ts). The ramp is computed in JS to concrete oklch() values (lib/theme/ramp.ts); rampVars(seed) sets the 16 --neutral-* vars on a scope and chrome re-resolves against them. Verified live: switching the seed turns the primary button cool (slate 264°) / warm (stone 49°) / green (forest 157°) and tints every surface, while badge markers stay constant.

Note: live seed-switching remounts the showcase (key={seed}) to dodge a Chrome stale-paint quirk for var()-chain colors on already-painted nodes. Statically-rendered pages (the real product) paint correctly from the start and don't need it.


Lean pass done

Verified: /foundation and /ds render the lean set; .h2 = 24px/600 on real components; build clean.


Sections + live page done

Section scaffolding — components/ds/sections.tsx

Token-only, lean-type, composed from the atoms: Nav (brand · links · CTA), Hero (eyebrow · display headline · sub · CTAs), FeatureGrid (icon cards), CTA band (inverted), Footer (columns). All flexible via props. Every value reads chrome vars, so a seed/theme/radius change re-skins all of them.

Live landing page — /site

A full marketing page composed from the sections, with a floating control (seed swatches + custom · light/dark · radius). Verified: switching the seed re-skins the entire page — hero, feature cards, the dark CTA band, footer — from cool (slate) to green (forest) etc., markers constant.

Ramp viewer in /ds

The generated neutral ramp for the current seed, shown as 12 swatches numbered 000–950, live as you pick a color.


Refinements (feedback) done


Still open small / later