@usetheo/ui 0.10.0-next.0 → 0.12.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +166 -3
  2. package/DESIGN.md +456 -0
  3. package/README.md +32 -21
  4. package/dist/chunk-BX7A5GUV.js +78 -0
  5. package/dist/chunk-BX7A5GUV.js.map +1 -0
  6. package/dist/{chunk-H3ANHVEL.js → chunk-DKQAHZG2.js} +4 -4
  7. package/dist/{chunk-H3ANHVEL.js.map → chunk-DKQAHZG2.js.map} +1 -1
  8. package/dist/{chunk-DAKIL5PC.js → chunk-IPEYGWA7.js} +3 -3
  9. package/dist/{chunk-DAKIL5PC.js.map → chunk-IPEYGWA7.js.map} +1 -1
  10. package/dist/chunk-MI5CXMZU.js +171 -0
  11. package/dist/chunk-MI5CXMZU.js.map +1 -0
  12. package/dist/chunk-QJGGTIUN.js +110 -0
  13. package/dist/chunk-QJGGTIUN.js.map +1 -0
  14. package/dist/chunk-R2PAGRDP.js +152 -0
  15. package/dist/chunk-R2PAGRDP.js.map +1 -0
  16. package/dist/{chunk-QU6RLHYH.js → chunk-TNBJ36XJ.js} +3 -3
  17. package/dist/{chunk-QU6RLHYH.js.map → chunk-TNBJ36XJ.js.map} +1 -1
  18. package/dist/chunk-ZNILW4G5.js +199 -0
  19. package/dist/chunk-ZNILW4G5.js.map +1 -0
  20. package/dist/components.css +1 -1
  21. package/dist/composites/agent-stream/index.js +3 -3
  22. package/dist/composites/data-table/index.js +10 -0
  23. package/dist/composites/data-table/index.js.map +1 -0
  24. package/dist/composites/page-shell/index.js +7 -0
  25. package/dist/composites/page-shell/index.js.map +1 -0
  26. package/dist/composites/rule-editor/index.js +2 -2
  27. package/dist/composites/skill-editor/index.js +2 -2
  28. package/dist/index.d.ts +281 -12
  29. package/dist/index.js +47 -42
  30. package/dist/index.js.map +1 -1
  31. package/dist/primitives/action-bar/index.js +4 -0
  32. package/dist/primitives/action-bar/index.js.map +1 -0
  33. package/dist/primitives/dropdown-menu/index.js +4 -0
  34. package/dist/primitives/dropdown-menu/index.js.map +1 -0
  35. package/dist/primitives/pin-input/index.js +4 -0
  36. package/dist/primitives/pin-input/index.js.map +1 -0
  37. package/llms.txt +7 -4
  38. package/package.json +114 -82
  39. package/registry/index.json +30 -0
  40. package/registry/r/action-bar.json +22 -0
  41. package/registry/r/data-table.json +27 -0
  42. package/registry/r/dropdown-menu.json +23 -0
  43. package/registry/r/page-shell.json +25 -0
  44. package/registry/r/pin-input.json +20 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,153 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.12.0-next.0] - 2026-05-25
11
+
12
+ Minor (additive, zero breaking change) — ships two LLM-facing artifacts
13
+ that complement the existing `llms.txt`: a structured visual spec
14
+ (`DESIGN.md`) and a companion agent skill (`skills/theo-ui/`).
15
+
16
+ ### Added
17
+
18
+ - **`DESIGN.md` at repo root (NEW)** — plain-text design system spec
19
+ for LLM assistants generating UI against `@usetheo/ui`. Follows the
20
+ awesome-design-md 9-section canonical structure (Visual Theme &
21
+ Atmosphere · Color Palette & Roles · Typography Rules · Layout
22
+ Principles · Depth & Elevation · Component Stylings · Responsive
23
+ Behavior · Do's and Don'ts · Agent Prompt Guide). Tokens mirror
24
+ `src/styles/tokens.css` and `src/themes/violet-forge.ts`. Shipped
25
+ via `package.json > files` alongside `llms.txt` and `CHANGELOG.md`
26
+ so consumers see the visual spec at `node_modules/@usetheo/ui/DESIGN.md`.
27
+ Reference research lives at
28
+ `.claude/knowledge-base/reference/design-md-convention.md`.
29
+ - **`skills/theo-ui/` companion agent skill (NEW)** — library-aware
30
+ design skill for AI coding assistants (Claude Code, Cursor, Codex,
31
+ OpenCode, Windsurf, Copilot) installable via the `vercel-labs/skills`
32
+ CLI:
33
+
34
+ ```
35
+ npx skills add usetheodev/theo-ui
36
+ ```
37
+
38
+ Four verbs (default build / `audit` / `migrate` / `catalog`), 32
39
+ universal slop-test gates plus surface-specific extensions, pre-emit
40
+ self-critique on six axes (Library-fit · Token-fidelity · Composition ·
41
+ A11y · Restraint · Voice), 12 page archetypes (P1–P12), 5 surfaces
42
+ (agent-chat · cloud-dashboard · settings-form · marketing · auth).
43
+ Project memory at `.theo-ui-skill/log.json`. 30 files, ~9300 lines
44
+ of markdown, ~416 KB. Distributed via the GitHub repo (not the npm
45
+ package). Root README ships Option C in Quickstart pointing
46
+ consumers at the skill.
47
+
48
+ ## [0.11.0-next.0] - 2026-05-25
49
+
50
+ Minor (additive, zero breaking change) — ships Brief #5 from the
51
+ TheoCloud dashboard team, closing 3 measured Deep Review findings.
52
+ Five new components: 3 brief-asks (PinInput, DataTable, PageShell)
53
+ + 2 explicit pre-requisites (DropdownMenu, ActionBar) that the
54
+ brief assumed existed but didn't.
55
+
56
+ Plan: `.claude/knowledge-base/plans/dashboard-primitives-brief-5-plan.md`
57
+ ADR: `.claude/knowledge-base/decisions/page-shell-composite-pattern.md`
58
+ Brief: `theo/docs/handoff/2026-05-25-theo-ui-cloud-dashboard-brief-5.md`
59
+
60
+ ### Added
61
+
62
+ - **`<DropdownMenu>` primitive (NEW, Brief #5 pre-req)** — accessible
63
+ menu built on `@radix-ui/react-dropdown-menu` (already a bundled
64
+ dep, no new peer needed). Sub-components attached via
65
+ `Object.assign`: `Trigger`, `Portal`, `Content`, `Item`,
66
+ `CheckboxItem`, `RadioItem`, `Label`, `Separator`, `Shortcut`,
67
+ `Group`, `Sub`, `SubTrigger`, `SubContent`, `RadioGroup`. Styled
68
+ with `@usetheo/ui` design tokens. Consolidates 5 prior direct-
69
+ Radix usages (`model-selector`, `intent-selector`, `agent-profile`,
70
+ `theme-switcher`, `theo-code-shell`) under a single styled
71
+ wrapper. 6 unit tests + 5 Ladle stories. SSR-safe.
72
+ - **`<ActionBar>` primitive (NEW, Brief #5 pre-req)** — page-top
73
+ action strip with three optional slots: search input (flex-1,
74
+ grows to fill), filter icon button, primary action button
75
+ (right-aligned). Returns `null` when no slots are provided.
76
+ Primary action supports `loading` state with `Loader2` spinner.
77
+ Usable standalone or composed inside `<PageShell>`. 6 unit tests
78
+ + 5 Ladle stories.
79
+ - **`<PinInput>` primitive (NEW)** — multi-slot OTP / code input
80
+ with auto-advance focus, paste handling (whitespace stripped),
81
+ arrow-key navigation, backspace clearing + focus back. Default
82
+ 6 slots, configurable. `inputMode="numeric"` (default, triggers
83
+ mobile numeric keyboard via `pattern="[0-9]*"`) or
84
+ `alphanumeric` (auto-uppercase). Optional `mask` renders bullets.
85
+ Optional `error` state applies destructive border. `onComplete`
86
+ fires once on transitions to complete (NOT on mount with pre-
87
+ filled value — verified via test). Closes Deep Review § 2.12 P2
88
+ (Verification page off-brand single-input pattern). 17 unit
89
+ tests + 7 Ladle stories.
90
+ - **`<DataTable>` composite (NEW)** — generic, sortable,
91
+ expandable composite over `<Table>` + `<Pagination>` +
92
+ `<Skeleton>` + `<EmptyState>` + `<DropdownMenu>`. Generic over
93
+ `T` (e.g. `DataTable<Domain>`). Sortable headers (controlled via
94
+ `onSortChange` OR uncontrolled client-side). Sticky header
95
+ (default true). Expandable rows with `expandable(row)` callback
96
+ — multi-row default, opt-in `expandMode="single"`. Row actions
97
+ via `rowActions(row)` opens DropdownMenu. Client-side pagination
98
+ with `pageSize`; sort changes reset to page 0. Loading state
99
+ renders 5 skeleton rows. Empty state delegates to `<EmptyState>`
100
+ or custom `emptyState` prop. Expanded row `colSpan` correctly
101
+ accounts for chevron + actions columns (EC-1 fix). pageSize<=0
102
+ clamps to 1 graceful degradation. Closes Deep Review Top-5
103
+ fix #2, § 2.2 P1, § 2.4 P1 (card-grid → sortable table for
104
+ Domains + Projects). 19 unit tests + 8 Ladle stories.
105
+ - **`<PageShell>` composite (NEW)** — page-level scaffold. Title
106
+ + optional description + optional ActionBar (when search /
107
+ primaryAction / onFilterClick provided), then one of four
108
+ mutually-exclusive content states with strict precedence:
109
+ loading > error > empty > children. Default loading is a
110
+ centered spinner Card; `loadingNode?` escape hatch for custom
111
+ skeletons. Error renders Card with message + optional retry
112
+ button + optional docs link. Empty delegates to `<EmptyState>`.
113
+ `aria-busy="true"` on the `<main>` element while loading. Does
114
+ NOT manage `document.title` (D3 scope-narrowing); consumers
115
+ wire `onTitleChange?` callback to their own hook. Dedupes
116
+ ~20 LOC × 13 dashboard pages of boilerplate. 15 unit tests + 6
117
+ Ladle stories.
118
+
119
+ ### Notes
120
+
121
+ - Edge-case review surfaced 1 MUST FIX (DataTable expanded row
122
+ colSpan miscalculation when rowActions present) + 14 SHOULD TEST
123
+ + 7 DOCUMENT — all incorporated into TDD blocks before
124
+ implementation.
125
+ - D3 scope-narrowing: PageShell does NOT include `useSetPageTitle`
126
+ / `PageMetaProvider` — those are consumer-scope hooks. The
127
+ library exposes only the visible heading + an `onTitleChange?`
128
+ callback for the consumer to wire their own title management.
129
+ - DropdownMenu consolidation is opt-in: the 5 existing direct-
130
+ Radix usages stay untouched in this release; migration is a
131
+ follow-up PR.
132
+ - Zero new peer-deps. `@radix-ui/react-dropdown-menu` was already
133
+ bundled.
134
+
135
+ ### Bundle delta (consumer canary, measured 2026-05-25)
136
+
137
+ Measured against TheoCloud dashboard (no consumer migration to the
138
+ new primitives yet — pure version bump):
139
+
140
+ | Metric | 0.10.0-next.0 | 0.11.0-next.0 | Δ |
141
+ |---|---|---|---|
142
+ | `@usetheo/ui` chunk | 10.96 KB brotli | 10.98 KB brotli | **+0.02 KB (+0.2%)** |
143
+ | TOTAL initial JS | 134.68 KB brotli | 135.56 KB brotli | +0.88 KB (+0.6%) |
144
+
145
+ Per-chunk cap (18 KB): passes with 7.02 KB headroom.
146
+ Total hard gate (180 KB): passes with 44.44 KB headroom.
147
+
148
+ The +0.02 KB chunk delta is effectively noise — confirms Brief #4's
149
+ per-component dist + tree-shaking works: 5 new components ship as
150
+ separate chunks and the consumer correctly drops all of them while
151
+ they aren't imported. Post-consumer-migration delta is expected at
152
+ +10-15 KB brotli (Brief #6 follow-up).
153
+
154
+ Evidence:
155
+ `.claude/knowledge-base/baselines/2026-05-26-post-brief-5/theocloud-bundle-delta.txt`
156
+
10
157
  ## [0.10.0-next.0] - 2026-05-25
11
158
 
12
159
  Minor (additive, zero breaking change) — fixes a publishing-pipeline
@@ -87,10 +234,26 @@ Brief: `theo/docs/handoff/2026-05-24-theo-ui-subpath-tree-shaking-brief-4.md`
87
234
  | **Shared chunks (`dist/chunk-*.js`)** | 0 | 119 | + |
88
235
 
89
236
  The barrel shrank because all component code now lives in shared
90
- chunks. Consumer-side bundle delta against TheoCloud dashboard:
91
- **TBD — see Phase 8 evidence file** at
237
+ chunks. **Consumer-side bundle delta against TheoCloud dashboard
238
+ (measured 2026-05-25):**
239
+
240
+ | Metric | 0.9.0-next.0 | 0.10.0-next.0 | Δ |
241
+ |---|---|---|---|
242
+ | `@usetheo/ui` chunk | 36.96 KB brotli | 10.96 KB brotli | **−26.00 KB (−70.3%)** |
243
+ | TOTAL initial JS | 176.27 KB brotli | 134.68 KB brotli | −41.59 KB (−23.6%) |
244
+
245
+ Per-chunk cap (50 KB): passes with 39 KB headroom (was 13 KB).
246
+ Total hard gate (240 KB): passes with 105 KB headroom (was 64 KB).
247
+
248
+ **Notable:** the savings were realized WITHOUT migrating consumer
249
+ imports to subpath form. The barrel benefits from tree-shaking now
250
+ because `dist/index.js` is structured as a collection of
251
+ per-component re-exports from shared chunks — Vite/Rollup's
252
+ tree-shaker can drop individual chunks per consumer usage. Subpath-
253
+ form migration is expected to yield additional savings on top.
254
+
255
+ Evidence file:
92
256
  `.claude/knowledge-base/baselines/2026-05-25-post-subpath/theocloud-bundle-delta.txt`
93
- once measured.
94
257
 
95
258
  ### Migration (consumer-side, opt-in)
96
259
 
package/DESIGN.md ADDED
@@ -0,0 +1,456 @@
1
+ # DESIGN.md — `@usetheo/ui` (Violet Forge)
2
+
3
+ > Plain-text design system spec for LLM assistants generating UI against `@usetheo/ui`. Companion to [`llms.txt`](./llms.txt) (component catalog) and [`docs/design-system.md`](./docs/design-system.md) (human-facing long-form). Read these three together; this file is the visual layer.
4
+ >
5
+ > Tokens here are normative — they mirror [`src/styles/tokens.css`](./src/styles/tokens.css) and [`src/themes/violet-forge.ts`](./src/themes/violet-forge.ts). Drift between this file and those is enforced in CI.
6
+
7
+ ---
8
+
9
+ ## 1. Visual Theme & Atmosphere
10
+
11
+ `@usetheo/ui` ships under the design system codename **Violet Forge** — a Vercel-inspired neutral-grayscale system with a Theo violet primary (`#7C3AED`) and a burnt-sienna accent (`#C96442`). The voice is **engineered, calm, agent-surface-ready**. Surfaces are pure neutrals (zero hue tint); color enters only through `primary`, `accent`, and semantic states (success / warning / destructive / info).
12
+
13
+ The system targets two adjacent surfaces:
14
+
15
+ 1. **AI agent surfaces** — chat threads, tool calls, agent timelines, streaming responses. These need legible monospace, dense information layouts, and visually quiet chrome.
16
+ 2. **Cloud dashboards** — deployment lists, environment configs, billing tables, settings panels. These need scannable data tables, predictable form rhythm, and elevation that signals action without shouting.
17
+
18
+ The default mode is **dark dominant** — `#0A0A0A` canvas with `#F5F5F5` foreground. Light mode is the polarity flip. The brand never mixes light + dark inside the same view; mode is a top-level decision.
19
+
20
+ **Anti-glass principle** (named guideline): Violet Forge does NOT use backdrop-filter blur, frosted-glass overlays, or chrome-glass effects as decorative depth. Elevation is built from theme-aware ink shadows (derived from `--foreground`) plus a primary-derived glow for signature actions. Reason: glass effects fight legibility on dense dashboards and inflate render cost on long-lived agent surfaces.
21
+
22
+ **Key characteristics**
23
+
24
+ - Pure-neutral surfaces (0% saturation on `background`, `card`, `secondary`, `muted`, `border`). All color comes from `primary` / `accent` / semantic tokens.
25
+ - Geist Sans + Geist Mono throughout. Three strict weights: 400 body, 500 UI, 600 display.
26
+ - 4-px base spacing scale; container caps at 1280 px.
27
+ - Theme-aware shadow tokens — they recolor when the theme swaps because they derive from `--foreground` (ink) and `--primary` (glow), not from baked hex.
28
+ - Density is a runtime tri-state (`compact` 32 px / `comfortable` 36 px / `spacious` 44 px control height) set on `<ThemeProvider>` or via `useDensity()`.
29
+ - 10 built-in themes (Violet Forge default + Classic Paper + Aurora Terminal + 7 RFC-0007 themes). Each is a frozen bundle of the same token slots — swapping themes never changes geometry or typography rules, only color values.
30
+
31
+ ---
32
+
33
+ ## 2. Color Palette & Roles
34
+
35
+ ### 2.1 Light mode
36
+
37
+ #### Surface
38
+
39
+ - **Background** (`{colors.background}` — `#FFFFFF`): page canvas. Pure white.
40
+ - **Card** (`{colors.card}` — `#FFFFFF`): card / dialog surface. Same as background — depth comes from elevation, not tint.
41
+ - **Popover** (`{colors.popover}` — `#FFFFFF`): floating layer (dropdowns, tooltips, hovercards).
42
+ - **Secondary** (`{colors.secondary}` — `#F5F5F5`): muted surface for nested cards, code-block backgrounds, hover lifts.
43
+ - **Muted** (`{colors.muted}` — `#F5F5F5`): identical to secondary; aliased for semantic clarity.
44
+ - **Border** (`{colors.border}` — `#E8E8E8`): hairline dividers, card edges, input borders.
45
+ - **Input** (`{colors.input}` — `#E8E8E8`): same as border; aliased for forms.
46
+
47
+ #### Text
48
+
49
+ - **Foreground** (`{colors.foreground}` — `#0A0A0A`): every heading and body paragraph.
50
+ - **Muted Foreground** (`{colors.muted-foreground}` — `#737373`): secondary text — captions, helper text, inactive nav.
51
+ - **Card Foreground** (`{colors.card-foreground}` — `#0A0A0A`): text on card surfaces.
52
+
53
+ #### Brand
54
+
55
+ - **Primary** (`{colors.primary}` — `#7C3AED`): Theo violet. Single canonical primary CTA color, focus-ring color, brand accent. Used as fill on primary buttons, badges, active tabs.
56
+ - **Primary Deep** (`{colors.primary-deep}` — `#5B21B6`): pressed / active state of primary.
57
+ - **Primary Glow** (`{colors.primary-glow}` — `#A78BFA`): hover halo, signature shadow component.
58
+ - **Primary Foreground** (`{colors.primary-foreground}` — `#FFFFFF`): text on primary surfaces.
59
+
60
+ #### Accent
61
+
62
+ - **Accent** (`{colors.accent}` — `#C96442`): burnt sienna. Celebratory secondary actions, rare-use highlight (success milestones, premium tier badges).
63
+ - **Accent Deep** (`{colors.accent-deep}` — `#9C4A2E`): pressed accent.
64
+ - **Accent Foreground** (`{colors.accent-foreground}` — `#FFFFFF`): text on accent surfaces.
65
+
66
+ #### Ring
67
+
68
+ - **Ring** (`{colors.ring}` — `#7C3AED`): focus ring — matches primary. Always 2 px width with 2 px offset.
69
+
70
+ #### Semantic
71
+
72
+ - **Success** (`{colors.success}` — `#16A34A`): positive confirmation, healthy status dots, build-passed badges.
73
+ - **Warning** (`{colors.warning}` — `#D97706`): caution, pending state, soft alerts.
74
+ - **Destructive** (`{colors.destructive}` — `#DC2626`): irreversible action, error states, danger zone CTAs.
75
+ - **Info** (`{colors.info}` — `#3B82F6`): informational callouts, neutral status indicators.
76
+
77
+ ### 2.2 Dark mode (dominant)
78
+
79
+ #### Surface
80
+
81
+ - **Background** (`{colors.background}` — `#0A0A0A`): page canvas.
82
+ - **Card** (`{colors.card}` — `#121212`): elevated surface — one step lighter than background.
83
+ - **Popover** (`{colors.popover}` — `#171717`): floating layer — one step lighter than card.
84
+ - **Secondary** (`{colors.secondary}` — `#1C1C1C`): nested cards, code blocks.
85
+ - **Muted** (`{colors.muted}` — `#1C1C1C`).
86
+ - **Border** (`{colors.border}` — `#292929`): hairline dividers.
87
+ - **Input** (`{colors.input}` — `#1C1C1C`).
88
+
89
+ #### Text
90
+
91
+ - **Foreground** (`{colors.foreground}` — `#F5F5F5`): every heading and body paragraph.
92
+ - **Muted Foreground** (`{colors.muted-foreground}` — `#999999`): Vercel gray-500 — secondary text.
93
+
94
+ #### Brand, Accent, Semantic (dark mode delta)
95
+
96
+ `primary` / `primary-deep` / `primary-glow` / `accent` / `accent-deep` keep the same hex values across modes — the brand identity is mode-invariant. Semantics shift toward the brighter end of the hue:
97
+
98
+ - **Success** — `#22E58C` (was `#16A34A`).
99
+ - **Warning** — `#F59E0B` (was `#D97706`).
100
+ - **Destructive** — `#FF4F6D` (was `#DC2626`).
101
+ - **Info** — `#5FB3FF` (was `#3B82F6`).
102
+
103
+ ### 2.3 Implementation note
104
+
105
+ All color tokens are stored as HSL triplets (`262 83% 58%`) in CSS custom properties, not hex. This enables alpha modulation via `hsl(var(--primary) / 0.4)` and `color-mix(in oklch, hsl(var(--primary)) 50%, transparent)`. Hex values shown above are the rendered equivalents.
106
+
107
+ ---
108
+
109
+ ## 3. Typography Rules
110
+
111
+ ### 3.1 Font families
112
+
113
+ - **Display + Body**: `Geist` — Vercel's open-source geometric sans. Loaded from Google Fonts with the full 100–900 weight axis; the design system uses only 400 / 500 / 600.
114
+ - **Mono**: `Geist Mono` — paired Vercel face. Code, paths, metrics, timestamps, terminal output, agent tool calls.
115
+
116
+ Fallbacks: `-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif` (display + body); `ui-monospace, SFMono-Regular, Menlo, monospace` (mono).
117
+
118
+ ### 3.2 Hierarchy
119
+
120
+ | Token | Size | Weight | Line Height | Letter Spacing | Use |
121
+ |---|---|---|---|---|---|
122
+ | `{typography.display-2xl}` | 64 px | 600 | 1.0 | -0.0464em | Hero headline (marketing surfaces only). |
123
+ | `{typography.display-xl}` | 48 px | 600 | 1.05 | -0.05em | Display headline — landing pages, empty-state heros. |
124
+ | `{typography.display-lg}` | 40 px | 600 | 1.1 | -0.05em | Section headline — splash dialogs. |
125
+ | `{typography.display-md}` | 32 px | 600 | 1.2 | -0.04em | Page title (PageShell `title` slot). |
126
+ | `{typography.headline}` | 28 px | 600 | 1.25 | -0.035em | Card cluster heads, settings section heads. |
127
+ | `{typography.title-lg}` | 24 px | 600 | 1.33 | -0.04em | Modal title, card title (`Card.Header`). |
128
+ | `{typography.title-md}` | 20 px | 600 | 1.4 | -0.03em | Sub-section title, inline heading. |
129
+ | `{typography.body-lg}` | 18 px | 400 | 1.56 | -0.01em | Lead paragraph under section headline. |
130
+ | `{typography.body-md}` | 14 px | 400 | 1.43 | 0 | Default body — paragraphs, list items, table cells. |
131
+ | `{typography.body-sm}` | 13 px | 400 | 1.46 | 0 | Helper text, captions, secondary metadata. |
132
+ | `{typography.label}` | 14 px | 500 | 1.43 | 0 | Button labels, form labels, nav links. |
133
+ | `{typography.label-caps}` | 12 px | 500 | 1.33 | 0.04em | Eyebrow caps, section dividers, badge uppercase. |
134
+ | `{typography.code-md}` | 14 px | 400 | 1.5 | 0 | Default code blocks, inline `<code>`. |
135
+ | `{typography.code-sm}` | 13 px | 500 | 1.54 | 0 | Tight code blocks, terminal output, agent tool calls. |
136
+
137
+ ### 3.3 Principles
138
+
139
+ - **Three strict weights**. The system uses only 400 / 500 / 600. Weight 700 / 800 is never used; the display ceiling is 600. This produces a calmer visual register than typical SaaS systems that lean on bold display weights.
140
+ - **Aggressive negative letter-spacing on display tier**. Every `display-*` and `title-*` token tracks negative (`-0.05em` to `-0.03em`). Reverting to default tracking visibly breaks the brand voice.
141
+ - **Sentence-case for headlines**. The system does not use ALL-CAPS headlines outside of `label-caps` (which is reserved for eyebrows). Page titles, section heads, dialog titles are all sentence case.
142
+ - **Mono only for the technical layer**. Code, paths, IDs (tenant_id, deployment_id), timestamps, terminal mockups, tool-call output. Body paragraphs never set in mono.
143
+ - **Tabular numerals on data cells**. `<code>` / `<pre>` / `<kbd>` / `<samp>` get `font-feature-settings: "tnum"` via the preset. Numbers in DataTable cells align column-wise.
144
+
145
+ ### 3.4 Font substitutes
146
+
147
+ The two faces (`Geist` and `Geist Mono`) are Apache-2.0-licensed and free for commercial use. No substitute is required for license reasons. If a consumer wants a different brand voice while keeping the design system geometry, swap to:
148
+
149
+ - **Geometric sans** — *Inter* (400 / 500 / 600) is the closest match. *Satoshi* is a passable second.
150
+ - **Mono** — *JetBrains Mono* (400) or *IBM Plex Mono* match the technical voice.
151
+
152
+ Theme switching covers this — `classic-paper` swaps to Inter, `aurora-terminal` swaps to Geist Mono as body.
153
+
154
+ ---
155
+
156
+ ## 4. Layout Principles
157
+
158
+ ### 4.1 Spacing scale
159
+
160
+ Base unit **4 px**. Tokens follow Tailwind's geometric scale.
161
+
162
+ | Token | Value | Use |
163
+ |---|---|---|
164
+ | `{spacing.1}` | 4 px | Inline gap between icon + label, tightest separation. |
165
+ | `{spacing.2}` | 8 px | Default inline gap (button row, badge row, chip row). |
166
+ | `{spacing.3}` | 12 px | Form-control internal padding, default `gap-3` row. |
167
+ | `{spacing.4}` | 16 px | Section gutter, card content gap. |
168
+ | `{spacing.5}` | 20 px | Card padding (`md` size, default density). |
169
+ | `{spacing.6}` | 24 px | Card padding (spacious density), section header gap. |
170
+ | `{spacing.8}` | 32 px | Section-to-section spacing inside a page. |
171
+ | `{spacing.10}` | 40 px | Page header gap below `PageShell.title`. |
172
+ | `{spacing.12}` | 48 px | Major section break inside long pages. |
173
+ | `{spacing.16}` | 64 px | Hero-band top/bottom padding. |
174
+ | `{spacing.20}` | 80 px | Landing-page section padding. |
175
+ | `{spacing.24}` | 96 px | Hero-section vertical rhythm. |
176
+ | `{spacing.32}` | 128 px | Top-of-page hero stretch. |
177
+
178
+ ### 4.2 Grid & container
179
+
180
+ - **Max width**: container caps at `1280 px` (the Tailwind `2xl` breakpoint). Content centers with `1 rem` horizontal padding at all sizes.
181
+ - **Column patterns** seen across composites:
182
+ - PageShell content area — single column, max 1280 px.
183
+ - DataTable — full-width, with sticky header and horizontal scroll on overflow.
184
+ - Dashboard grids — 1-up (mobile) → 2-up (tablet) → 3-up (desktop) for card clusters.
185
+ - ChatThread / AgentTimeline — single column with internal max-width ~768 px for readability.
186
+
187
+ ### 4.3 Density tri-state (runtime)
188
+
189
+ | Density | Control height (md tier) | Textarea min-h | Card `md` padding | Body text |
190
+ |---|---|---|---|---|
191
+ | `compact` | 32 px (`h-8`) | 96 px | 20 px | 14 px |
192
+ | `comfortable` *(default)* | **36 px** | 96 px | 20 px | **14 px** |
193
+ | `spacious` | 44 px (`h-11`) | 128 px | 24 px | 14 px |
194
+
195
+ Set globally via `<ThemeProvider defaultDensity="compact">` or runtime via `useDensity()`. Density only affects the `md` size tier — explicit `size="sm"` / `size="lg"` overrides density.
196
+
197
+ ### 4.4 Whitespace philosophy
198
+
199
+ The system reads as engineered — large outer gaps + tight interior gaps, never the other way around. Section-to-section uses `{spacing.8}`–`{spacing.12}` (32–48 px). Inside a card, the title-to-body gap is `{spacing.2}` (8 px); body-to-CTA gap is `{spacing.4}` (16 px). The page's calm cadence comes from this contrast.
200
+
201
+ ---
202
+
203
+ ## 5. Depth & Elevation
204
+
205
+ Elevation is a numbered ladder. Theme-aware — every shadow derives from `--foreground` (ink) and `--primary` (glow), so swapping themes recolors them automatically.
206
+
207
+ | Level | Treatment | Use |
208
+ |---|---|---|
209
+ | Level 0 — Flat | No shadow, no border. | Full-bleed bands, hero surfaces. |
210
+ | Level 1 — Hairline | `1 px` solid `{colors.border}`. | Default card chrome, input borders, table dividers. |
211
+ | Level 2 — Subtle (`shadow-sm`) | `0 1px 2px 0 hsl(var(--foreground) / 0.06)`. | Slightly elevated cards (deployment rows, table headers). |
212
+ | Level 3 — Medium (`shadow-md`) | `0 2px 8px -2px hsl(var(--foreground) / 0.08), 0 1px 3px hsl(var(--foreground) / 0.06)`. | Floating cards (hover state), popovers, dropdowns. |
213
+ | Level 4 — High (`shadow-lg`) | `0 12px 32px -8px hsl(var(--foreground) / 0.12), 0 4px 12px hsl(var(--foreground) / 0.08)`. | Modals, command palettes, drawer surfaces. |
214
+ | Level 5 — Glow (`shadow-glow`) | `0 0 24px hsl(var(--primary) / 0.25)`. | Primary button hover state, signature emphasis. |
215
+ | Level 6 — Strong glow (`shadow-glow-strong`) | `0 0 32px hsl(var(--primary) / 0.4)`. | Primary button focus + hover combined, "now playing" emphasis. |
216
+
217
+ ### 5.1 Decorative depth (separate from elevation)
218
+
219
+ - **Dark-mode polarity flip** is the system's chief depth cue between sections. Switching a band from `{colors.background}` to `{colors.card}` (one step lighter in dark mode, identical in light mode) signals a new "depth zone."
220
+ - **Primary glow** is the only non-ink decoration. Reserved for the canonical primary CTA hover state. Never apply it to secondary buttons, cards, or surfaces — it's the brand signature.
221
+ - **No glass / backdrop-filter**. Violet Forge does not use frosted glass, backdrop blur, or chrome-glass effects. Stated explicitly in `docs/design-system.md > Anti-glass guideline`.
222
+
223
+ ---
224
+
225
+ ## 6. Component Stylings
226
+
227
+ The library exposes 121 components (92 primitives + 29 composites). The set below is the spec-defining subset — patterns here propagate to the rest.
228
+
229
+ ### 6.1 Buttons
230
+
231
+ **`<Button variant="primary">`** — canonical Theo CTA.
232
+ - Background `{colors.primary}`, text `{colors.primary-foreground}`, label set in `{typography.label}`, padding `{spacing.3}` horizontal, height tracks density (default 36 px), shape `{rounded.lg}` (10 px).
233
+ - Hover: adds `{shadow.glow}` (no fill change).
234
+ - Active: `bg-primary-deep`, `scale-[0.98]`, removes glow.
235
+ - Disabled: `opacity-50`, `pointer-events-none`.
236
+
237
+ **`<Button variant="secondary">`** — paired secondary action.
238
+ - Background `{colors.secondary}`, text `{colors.secondary-foreground}`, `1 px` `{colors.border}` border. Hover: `bg-muted`. Active: `scale-[0.98]`.
239
+
240
+ **`<Button variant="accent">`** — celebratory / premium action.
241
+ - Background `{colors.accent}`, text `{colors.accent-foreground}`. Hover: `bg-accent-deep`. Use sparingly.
242
+
243
+ **`<Button variant="ghost">`** — minimal action, embedded in dense rows.
244
+ - Background `transparent`, text `{colors.foreground}`. Hover: `bg-muted`. Active: `bg-secondary` + `scale-[0.98]`.
245
+
246
+ **`<Button variant="link">`** — inline text action.
247
+ - Background `transparent`, text `{colors.primary}`, `underline-offset-4`. Hover: `text-primary-deep` + underline. Height auto, padding zero.
248
+
249
+ **`<Button variant="destructive">`** — irreversible action.
250
+ - Background `{colors.destructive}`, text `{colors.destructive-foreground}`. Hover: `bg-destructive/90`. Active: `scale-[0.98]`. Used inside `<DangerZone>` composites and `<ConfirmDialog>` destructive flows.
251
+
252
+ **Sizes**: `sm` (32 px), `md` (36 px default, density-aware), `lg` (48 px), `icon` (square at md height).
253
+
254
+ ### 6.2 Cards & containers
255
+
256
+ **`<Card>`** — universal container primitive.
257
+ - Background `{colors.card}`, text `{colors.card-foreground}`, border `1 px` `{colors.border}`, shape `{rounded.xl}` (14 px), padding `{spacing.5}` (20 px) at `md` size.
258
+ - Sub-components: `Card.Header`, `Card.Title` (`{typography.title-lg}`), `Card.Description` (`{typography.body-sm}` + `text-muted-foreground`), `Card.Content`, `Card.Footer`.
259
+
260
+ **`<Dialog>` (modal surface)**.
261
+ - Background `{colors.popover}`, shape `{rounded.xl}`, padding `{spacing.6}` (24 px), elevation Level 4 (`shadow-lg`). Backdrop is `bg-black/80` (no blur — anti-glass principle).
262
+
263
+ **`<Popover>` / `<DropdownMenu>` / `<Tooltip>`**.
264
+ - Background `{colors.popover}`, shape `{rounded.lg}` (10 px), elevation Level 3 (`shadow-md`), border `1 px` `{colors.border}`.
265
+
266
+ ### 6.3 Inputs & forms
267
+
268
+ **`<Input>` / `<Textarea>` / `<Select.Trigger>`**.
269
+ - Background `{colors.input}`, text `{colors.foreground}`, border `1 px` `{colors.border}`, shape `{rounded.md}` (6 px), padding `{spacing.3}` (12 px) horizontal, height tracks density (36 px default).
270
+ - Focus: ring `{colors.ring}` (matches primary), `ring-2` `ring-offset-2`.
271
+ - Disabled: `opacity-50`, `cursor-not-allowed`.
272
+
273
+ **`<Checkbox>` / `<Switch>` / `<RadioGroup>`**.
274
+ - Effective tap area ≥ 24×24 CSS px (WCAG 2.5.8 AA floor); visual size 16 px, with padding extending the focus zone.
275
+ - Checked state: `bg-primary` fill, `border-primary`.
276
+
277
+ **`<PinInput>`** (Brief #5 — agent verification, OTP).
278
+ - 4 / 6 / 8-digit grid. Each slot is a `<Input>` styled `text-center` `{typography.title-md}` with auto-advance on key press.
279
+
280
+ ### 6.4 Data display
281
+
282
+ **`<DataTable>`** (composite).
283
+ - Header row uses `{typography.label-caps}` (12 px, uppercase, weight 500) with `text-muted-foreground`. Header background `{colors.card}`.
284
+ - Body cells use `{typography.body-md}` (14 px), padding `{spacing.3}` (12 px) vertical, `{spacing.4}` (16 px) horizontal.
285
+ - Row dividers: `1 px` `{colors.border}`.
286
+ - Hover row: `bg-secondary`.
287
+ - Sticky header on scroll. Selectable rows show a `{colors.primary}` ring on the leftmost cell.
288
+
289
+ **`<Badge>`**.
290
+ - Default variant: `bg-secondary`, `text-secondary-foreground`, `{rounded.md}` (6 px), padding `{spacing.2}` (8 px) horizontal, height auto, `{typography.label-caps}` or `{typography.body-sm}`.
291
+ - Variants: `primary` / `accent` / `success` / `warning` / `destructive` / `info` / `outline` — fill follows the semantic color.
292
+
293
+ **`<StatusDot>` / `<StatTile>` / `<UsageMeter>` / `<Progress>`**.
294
+ - Dashboard primitives. Status colors map directly to `success` / `warning` / `destructive` / `info` semantic tokens. Numeric values in `{typography.display-md}` or `{typography.title-md}` with `font-mono` for the digits.
295
+
296
+ ### 6.5 Navigation
297
+
298
+ **`<PageShell>`** (composite — Brief #5).
299
+ - Owns the page header (title + description + optional ActionBar) and the state precedence (loading > error > empty > children). Title uses `{typography.display-md}` (32 px), description uses `{typography.body-sm}` `text-muted-foreground`.
300
+ - Reserves `{spacing.10}` (40 px) gap below the header before children.
301
+
302
+ **`<ActionBar>`** (Brief #5).
303
+ - Horizontal flex row: search input (grows `flex-1`), optional filter icon button, optional primary action button right-aligned. Returns `null` when empty. Composes inside `PageShell` or standalone.
304
+
305
+ **`<DropdownMenu>` / `<CommandPalette>`**.
306
+ - Popover surface, elevation Level 3, items use `{typography.body-md}`, padding `{spacing.2}` (8 px) vertical, `{spacing.3}` (12 px) horizontal. Active/highlighted item: `bg-secondary`. Destructive items: `text-destructive`.
307
+
308
+ ### 6.6 Agent surfaces (signature components)
309
+
310
+ **`<ChatMessage>`** — message bubble with `parts[]` API (text / tool-call / tool-result / file / image).
311
+ - Background `{colors.card}`, text `{colors.foreground}`, padding `{spacing.4}` (16 px), shape `{rounded.lg}` (10 px). Role-based variants: `user` (right-aligned, `bg-primary/10`), `assistant` (left-aligned, `bg-card`), `system` (full-width, muted).
312
+ - Markdown rendering via the bundled engine. Code blocks use `<CodeBlock>` (mono, syntax highlight).
313
+
314
+ **`<AgentEvent>` / `<ToolCall>` / `<ToolResult>`**.
315
+ - Compact inline-block surfaces inside the chat stream. Background `{colors.secondary}`, text `{colors.foreground}`, shape `{rounded.md}` (6 px), padding `{spacing.3}` (12 px). Mono labels (`{typography.code-sm}`), prose body (`{typography.body-sm}`).
316
+
317
+ **`<CodeBlock>`**.
318
+ - Background `{colors.secondary}` (or `{colors.popover}` in dark mode for slightly deeper register), shape `{rounded.lg}` (10 px), padding `{spacing.4}` (16 px), `{typography.code-md}`. Syntax highlighting via Shiki (optional peer-dep).
319
+
320
+ **`<AgentTimeline>` / `<AgentStream>`**.
321
+ - Vertical list of events with a left-aligned status dot per row. Time stamps use `font-mono` and `text-muted-foreground`.
322
+
323
+ ---
324
+
325
+ ## 7. Responsive Behavior
326
+
327
+ ### 7.1 Breakpoints (Tailwind defaults)
328
+
329
+ | Name | Width | Key changes |
330
+ |---|---|---|
331
+ | `sm` | ≥ 640 px | Stacked layouts unstack. Side-by-side button rows resume. |
332
+ | `md` | ≥ 768 px | Two-column grids enable. Nav stays horizontal. |
333
+ | `lg` | ≥ 1024 px | Three-column grids enable. PageShell + sidebar layout common. |
334
+ | `xl` | ≥ 1280 px | Container caps here. Full dashboard layouts. |
335
+ | `2xl` | ≥ 1536 px | Content stays centered at 1280 px. Bands stretch edge-to-edge in color. |
336
+
337
+ ### 7.2 Touch targets (WCAG 2.5.8 AA)
338
+
339
+ The system targets **WCAG 2.5.8 Level AA** — minimum 24×24 CSS px effective tap area. The default `comfortable` density (36 px control height) comfortably exceeds this. Compact mode (32 px) still meets AA because the 2 px focus ring on each side expands the focusable area to ~36×36 effective.
340
+
341
+ The system does **not** target 2.5.5 Level AAA (44 px) at `comfortable`. Consumers requiring AAA can opt into `spacious` mode globally or use `size="lg"` per call site.
342
+
343
+ ### 7.3 Collapsing strategy per signature composite
344
+
345
+ - **`<PageShell>`** — title stays `{typography.display-md}` across all breakpoints. `<ActionBar>` collapses from horizontal flex to stacked at `<sm`. Search input always grows `flex-1`.
346
+ - **`<ChatThread>`** — internal max-width 768 px. Below `sm`, padding tightens from `{spacing.4}` to `{spacing.3}`.
347
+ - **`<DataTable>`** — sticky header preserved. Below `md`, low-priority columns hide (consumer specifies via column `hideBelow` prop) and remaining columns enable horizontal scroll.
348
+ - **`<DropdownMenu>` / `<CommandPalette>`** — full-screen drawer below `sm`, floating popover at `≥ sm`.
349
+ - **`<Dialog>`** — full-screen below `sm`, centered modal `≥ sm`. Max-width ~600 px at `lg`.
350
+
351
+ ### 7.4 Reduced motion
352
+
353
+ The token layer respects `prefers-reduced-motion: reduce` — all `transition-*` durations neutralize, scale transforms remove, `shadow-glow` becomes a static border. Consumers don't opt in; it's automatic.
354
+
355
+ ---
356
+
357
+ ## 8. Do's and Don'ts
358
+
359
+ ### Do
360
+
361
+ - **Reserve `{colors.primary}` for the canonical Theo CTA.** Primary buttons, focus rings, active tab indicators, brand accents. Never for body text, never for body backgrounds.
362
+ - **Use the density tri-state as a global preference**, not as a per-component override. Set `<ThemeProvider defaultDensity="compact">` once and let the system propagate.
363
+ - **Compose composites from primitives via the barrel**, not by re-implementing layout primitives. A new dashboard page is `<PageShell>` + `<ActionBar>` + `<DataTable>`, not custom flex/grid.
364
+ - **Set every code surface and technical label in `{typography.code-*}` (Geist Mono).** Tool calls, file paths, IDs, timestamps, terminal output.
365
+ - **Pair Do's and Don'ts when adding a new component spec.** Each component should declare its allowed states and its forbidden states.
366
+ - **Layer theme-aware shadows over hardcoded ones.** Use `{shadow.sm/md/lg/glow}` tokens; never write `0 4px 12px rgba(0,0,0,0.1)` inline.
367
+ - **Use sentence-case for headlines.** Page titles, dialog titles, card titles. ALL-CAPS is reserved for `label-caps` eyebrows only.
368
+
369
+ ### Don't
370
+
371
+ - **Don't introduce a sixth surface tint.** The system operates on background / card / popover / secondary / muted — five neutrals. New tints flatten the elevation language.
372
+ - **Don't promote Geist to weight 700.** The display ceiling is 600. The calm visual register depends on this.
373
+ - **Don't use `backdrop-filter: blur(…)` or glass effects.** Anti-glass principle is named in `docs/design-system.md > Principles`. Elevation is built from ink shadows + glow, never from blur.
374
+ - **Don't render body paragraphs in `{font.mono}`.** Mono is for the technical layer only.
375
+ - **Don't apply `{shadow.glow}` to secondary buttons, cards, or surfaces.** The glow is the primary CTA signature; spreading it dilutes the brand.
376
+ - **Don't use emojis in component labels, button text, error messages, or markdown content authored by the system.** Consumers' user content may contain emojis (chat messages, names) — that's user data, not authored UI.
377
+ - **Don't reference external brand names (Vercel, Linear, etc.) as if endorsed.** When citing inspiration in docs or theme descriptions, prefix with "Inspired by, not affiliated with" — see `seven-themes-edge-cases-2026-05-22.md` for the trademark rule.
378
+ - **Don't pair the `comfortable` density on one screen with `spacious` controls on another.** Density is a global choice — pick one tier and stay consistent within a surface.
379
+
380
+ ---
381
+
382
+ ## 9. Agent Prompt Guide
383
+
384
+ Quick fragments for LLM assistants generating UI against `@usetheo/ui`. Drop into a prompt verbatim.
385
+
386
+ ### 9.1 Quick token reference
387
+
388
+ ```
389
+ PRIMARY var(--primary) hsl(262 83% 58%) #7C3AED Theo violet
390
+ ACCENT var(--accent) hsl(15 54% 53%) #C96442 Burnt sienna
391
+ FOREGROUND var(--foreground) hsl(0 0% 4%) #0A0A0A Ink (light) / inverted in dark
392
+ BACKGROUND var(--background) hsl(0 0% 100%) #FFFFFF Canvas
393
+ MUTED-FG var(--muted-foreground) hsl(0 0% 45%) #737373 Secondary text
394
+ BORDER var(--border) hsl(0 0% 91%) #E8E8E8 Hairline
395
+ SUCCESS var(--success) hsl(142 71% 36%) #16A34A
396
+ WARNING var(--warning) hsl(33 92% 44%) #D97706
397
+ DESTRUCTIVE var(--destructive) hsl(0 72% 51%) #DC2626
398
+ INFO var(--info) hsl(217 91% 60%) #3B82F6
399
+
400
+ FONT-DISPLAY Geist (weights 400/500/600)
401
+ FONT-BODY Geist (weights 400/500/600)
402
+ FONT-MONO Geist Mono (weights 400/500/600)
403
+
404
+ SPACING 4 px base — space-1 (4) … space-32 (128). Default control height 36 px.
405
+ RADIUS sm 4 / md 6 / lg 10 / xl 14 / 2xl 20 / full 9999
406
+ ```
407
+
408
+ ### 9.2 Prompt: build a dashboard list page
409
+
410
+ > Build a [DOMAIN] list page using `@usetheo/ui`. Compose `<PageShell title="…" description="…">` with an `<ActionBar>` (search input + primary action button). Inside, render `<DataTable>` with sticky header. Use `{typography.display-md}` for the title via PageShell's built-in. Status indicators use `<StatusDot variant="success|warning|destructive|info">`. Row actions use `<DropdownMenu>` with `variant="ghost"` trigger. No emojis, no inline hex — only token references. Match the `comfortable` density default.
411
+
412
+ ### 9.3 Prompt: build a settings panel
413
+
414
+ > Build a settings page using `@usetheo/ui`. Wrap content in `<PageShell title="Settings" description="…">`. Inside, stack `<Card>` sections per setting group. Each Card has `Card.Header` (title `{typography.title-lg}` + description `{typography.body-sm} text-muted-foreground`), `Card.Content` with form fields (`<Input>`, `<Switch>`, `<Select>`), and `Card.Footer` with a `<Button variant="primary">` save action. Destructive actions go in a final `<DangerZone>` composite. Spacing between Cards is `{spacing.6}` (24 px).
415
+
416
+ ### 9.4 Prompt: build an agent chat surface
417
+
418
+ > Build a chat surface using `@usetheo/ui`. Wrap in a flex column with internal max-width 768 px. Render messages via `<ChatMessage role="user|assistant|system" parts={…}>`. Tool calls and tool results inside `parts[]` render as `<ToolCall>` / `<ToolResult>` blocks (mono labels via `{typography.code-sm}`, prose via `{typography.body-sm}`). Use `<AgentEvent>` for non-message stream events. Composer at the bottom uses `<ChatComposer>` (a composite with `<Textarea>` + send button). No emojis. Streaming state uses `<AgentStreaming>`.
419
+
420
+ ### 9.5 Prompt: build a billing / pricing surface
421
+
422
+ > Build a pricing/billing page using `@usetheo/ui`. Wrap in `<PageShell title="Billing" description="…">`. Pricing tiers render as a 3-up grid of `<Card>` (tablet 2-up, mobile 1-up). Featured tier uses `<PlanBadge variant="primary">` and an outline on the Card (`border-primary`). Tier name in `{typography.title-lg}`, price in `{typography.display-xl}` with `font-mono` for digits, feature list in `{typography.body-md}` rows with a `<Check>` icon. CTA at the bottom: `<Button variant="primary" size="lg">` for the featured tier, `<Button variant="secondary" size="lg">` for the rest. Usage meters use `<UsageMeter>` and `<CostMeter>`.
423
+
424
+ ### 9.6 Prompt: use the design system tokens
425
+
426
+ > All styling must use `@usetheo/ui` tokens. Colors via Tailwind preset classes (`bg-primary`, `text-foreground`, `border-border`) or CSS vars (`hsl(var(--primary))`). Typography via preset (`text-display-md`, `text-body-md`, `text-label-caps`) — never raw `text-4xl`. Spacing via Tailwind utilities (`gap-4`, `p-5`) which map to the 4-px base. Radii via `rounded-lg` / `rounded-xl` / `rounded-md` mapped to system tokens. Never inline hex, never inline pixel values for spacing — always tokens. The `@usetheo/ui` Tailwind preset must be installed via `presets: [theoUiPreset]` in `tailwind.config.{ts,js}`.
427
+
428
+ ### 9.7 Component subpath import map (post-Brief-4 tree-shaking)
429
+
430
+ For Tailwind v4 / Vite projects, prefer subpath imports — they tree-shake per-component:
431
+
432
+ ```ts
433
+ import { Button } from "@usetheo/ui/button";
434
+ import { Card } from "@usetheo/ui/card";
435
+ import { Input } from "@usetheo/ui/input";
436
+ import { DataTable } from "@usetheo/ui/data-table";
437
+ import { PageShell } from "@usetheo/ui/page-shell";
438
+ import { ActionBar } from "@usetheo/ui/action-bar";
439
+ import { ChatMessage } from "@usetheo/ui/chat-message";
440
+ ```
441
+
442
+ Barrel imports (`import { Button } from "@usetheo/ui"`) work but ship the full barrel — acceptable for prototyping, not for production bundles.
443
+
444
+ ---
445
+
446
+ ## See also
447
+
448
+ - [`README.md`](./README.md) — package overview + install instructions
449
+ - [`llms.txt`](./llms.txt) — component catalog + anti-patterns + import recipes
450
+ - [`docs/design-system.md`](./docs/design-system.md) — long-form spec with ADR links
451
+ - [`CLAUDE.md`](./CLAUDE.md) — locked names, voice rules, quality gates
452
+ - [`CHANGELOG.md`](./CHANGELOG.md) — version history
453
+
454
+ ---
455
+
456
+ **End of DESIGN.md** — Violet Forge, `@usetheo/ui` 0.11.0-next.0