Skip to content
Docs

Command palette

Jump to a page or view, or find a todo by name.

Changelog

Design patterns

Which design source, kit and agent skill each surface uses, and the rules that keep a rebrand cheap.

Markdown

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

RegisterSurfacesAuthorityAgent skills
Operatedashboard, todos, files, settings, authshadcn primitives and tokens; apps/web/PRODUCT.mdui-catalog, web-design-guidelines
Persuadelanding, marketing, pricingdocs/landing.md; the same tokens through landing.css aliasesdesign-taste-frontend, redesign-existing-projects, stageshow
Developer docs/docsFumadocs headless (fumadocs-core + fumadocs-mdx) with our own componentsdocs-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

JobSourceSkill
Primitive, form, overlay, empty, toastshadcn/ui, installed in packages/ui/src/componentsshadcn, ui-catalog
Free composites (tree, timeline, pickers)ReUI free registry (@reui); Pro blocks stay outui-catalog
Chat, streaming, tool cardsthe installed AI/chat set, then AI Elementsai-assistant
Charts, sparklines, KPI cardsBklit UI, the @bklit shadcn registry (MIT, source copied into the app)bklit-ui, bklit-ui-design, bklit-ui-migrate
Product illustrations (faux UI)Stageshow, kit vendored in apps/web/src/platform/stageshow/stageshow
Loading states (skeletons)skullet through MeasuredSkeleton (apps/web/src/platform/skeleton/) and samplesui-catalog
Motion on an existing controltransitions.dev recipestransitions-dev, emil-design-eng, review-animations
Native (Expo)SwiftUI / Jetpack Compose through @expo/uiexpo-ui, expo-native-ui

Three rules with no alternative

  • Charts come from Bklit UI only. Every chart, sparkline and KPI or stat card on the web is a Bklit component, installed with pnpm dlx shadcn@latest add @bklit/<item> in packages/ui. No other chart library (Recharts, Chart.js, Nivo, Victory, visx, ECharts, ApexCharts, Highcharts, Tremor and the like) is imported in apps/web/src or packages/ui/src or listed in their package.json; scripts/web-chart-library.test.mjs fails if one is. d3 stays allowed, because Bklit components call it. An existing chart moves over with bklit-ui-migrate.
  • Product illustrations are built with Stageshow only. Faux product UI on landing, marketing and docs pages (stills, feature cards, cursor demos, bento grids) starts from a Stageshow gold example, uses the kit in apps/web/src/platform/stageshow/ (copy more parts from .agents/skills/stageshow/kit when a scene needs them) and ships only after its loop passes film.mjs --gate. A chart inside a scene is a Bklit chart placed on the stage, since Stageshow takes the app's own components. Review holds this rule.
  • Loading skeletons are skullet, through MeasuredSkeleton only. Every loading skeleton in the web app is MeasuredSkeleton from @/platform/skeleton/measured-skeleton: pass data and a sample of the same shape from the feature's <feature>.samples.ts and render from the argument of children, or pass loading over content already on screen (a refetch). The wrapper adds the translated label; the provider in the signed-in layout sets the reveal and the theme, and the colours are the --skullet-* tokens. Only apps/web/src/platform/skeleton/ imports skullet. No hand-made animate-pulse boxes, no shadcn Skeleton or SidebarMenuSkeleton, and no other skeleton library (react-loading-skeleton, react-content-loader, MUI, Chakra and the like) in apps/web/src or packages/ui/src or their package.json. Skeletons render only behind sign-in, so the skullet chunk stays off public pages (W15): a public page renders its data from the route loader, or a Spinner for a pending action. scripts/web-skeletons.test.mjs checks all of this, including that no route outside routes/_auth/ reaches the wrapper through its imports.

Bklit draws DOM and SVG for React on the web and has no React Native build, so the chart rule stops at apps/web and packages/ui. A chart in the Expo app uses victory-native, which draws with Skia, Reanimated and Gesture Handler, all already in apps/mobile; add it with expo install with the first mobile chart (it needs a newer Skia than the app pins today). An iOS-only screen may use Chart from @expo/ui/swift-ui (Swift Charts) instead.

Skullet measures the DOM and ships React, Vue, Svelte and web-component builds, but none for React Native, so the skeleton rule stops at the web too. A loading list in the Expo app uses SkeletonRows from apps/mobile/src/design (a still placeholder with the metrics of a SettingRow group and a spoken label).

The shadcn skeleton.tsx stays in packages/ui because the vendored shadcn sidebar (SidebarMenuSkeleton) and the ReUI emoji picker draw their own loading state with it; app code does not import either. The wrapper covers skullet's Skeleton only: a Suspense fallback (SkeletonSuspense) or a placeholder box (SkeletonBox) gets its own wrapper in platform/skeleton/ when a screen first needs one, never a direct import. Passing id keeps a skeleton's shape in localStorage; the change that first does so also calls forgetShapes() on sign-out.

Two surfaces predate these rules: the dashboard KPI cards (dashboard.stat-card.tsx, plain number cards) and the hand-built fragments of the landing feature board (landing.feature-*.tsx). Do not copy them as patterns; move them to Bklit and Stageshow when you rework them.

Rules that keep it consistent

  • Tokens, not values. A component reads --primary, --card, --radius and 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's platform/ 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 landing, marketing and docs pages 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 --gate before 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/.

Key files

Edit on GitHubDocumentation is in English.
Cookie settings
We use essential cookies to make our app work. With your consent, we also use optional cookies to improve your experience.

Read our Cookie Policy and Privacy Policy.