The template's visual identity lives in tokens (packages/ui/src/styles/globals.css), and every surface composes from owned source in packages/ui. apps/web/PRODUCT.md holds the principles and anti-references; read it before any UI work. This page says which tool each job uses, so a project built from the template keeps one design language as it grows.
Three registers
| Register | Surfaces | Authority | Agent skills |
|---|---|---|---|
| Operate | dashboard, todos, files, settings, auth | shadcn primitives and tokens; apps/web/PRODUCT.md | ui-catalog, web-design-guidelines |
| Persuade | landing, marketing, pricing | docs/landing.md; the same tokens through landing.css aliases | design-taste-frontend, redesign-existing-projects, stageshow |
| Developer docs | /docs | Fumadocs headless (fumadocs-core + fumadocs-mdx) with our own components | docs-and-changelog |
An agent picks the register first (ui-catalog step 1), then loads only that register's skills. Taste guidance never reaches an Operate screen.
Sources by job
| Job | Source | Skill |
|---|---|---|
| Primitive, form, overlay, empty, toast | shadcn/ui, installed in packages/ui/src/components | shadcn, ui-catalog |
| Free composites (tree, timeline, pickers) | ReUI free registry (@reui); Pro blocks stay out | ui-catalog |
| Chat, streaming, tool cards | the installed AI/chat set, then AI Elements | ai-assistant |
| Charts, sparklines, KPI cards | Bklit UI, the @bklit shadcn registry (MIT, source copied into the app) | bklit-ui, bklit-ui-design, bklit-ui-migrate |
| Faux product UI on the landing | Stageshow, kit vendored in apps/web/src/platform/stageshow/ | stageshow |
| Loading states (skeletons) | skullet, wrapped in apps/web/src/platform/skeleton/ with sample data per feature | — |
| Motion on an existing control | transitions.dev recipes | transitions-dev, emil-design-eng, review-animations |
| Native (Expo) | SwiftUI / Jetpack Compose through @expo/ui | expo-ui, building-native-ui |
No screen draws a chart yet. The shadcn chart wrapper (Recharts) is installed but unused; a new chart comes from @bklit, and bklit-ui-migrate moves a Recharts chart over.
Rules that keep it consistent
- Tokens, not values. A component reads
--primary,--card,--radiusand the rest; a registry's own colors and fonts are mapped onto them, never copied. A kit with its own token layer (Stageshow's--ss-*, Bklit's chart colors) is aliased to ours in one place. - Owned source. Registry items are copied into
packages/ui(or a feature'splatform/folder) and exported one subpath at a time. Diff before accepting an overwrite: local files carry React Compiler and test edits. - Every state ships. Loading, empty and error are part of the screen, because developers copy what the template shows.
- A skeleton is drawn from the real component. A loading state is measured from a sample of the component it stands in for (
<feature>.samples.ts), never hand-built from boxes, so nothing moves when the data lands. - Honest illustrations. Faux UI on the landing uses the product's own strings from the locale catalogs, shows only features the template has, and is labelled as an illustration. A Stageshow scene passes
film.mjs --gatebefore it ships. - Motion respects the reader. Every animation has a reduced-motion state, pauses off screen, and starts when it can be seen.
- Weight is measured. A new kit or registry item records its gzip delta on the route it lands in, in
docs/measurements/.