Design tokens, components and conventions that keep designers and engineers speaking the same language — from Figma to production in WestyApp.
One system, three audiences. The tokens, variants and import surface below are the shared contract.
Every color, radius and type decision here has a name. Design with those names — primary, muted, destructive — and dark mode comes for free, because each token remaps itself.
If the token you need doesn't exist, the fix is a new token — never a hardcoded color.
import { Button } from
"@workspace/ui/components/button"The release branch ships compiled ESM and TypeScript declarations. react, react-dom and next-themes are peer dependencies: your app owns the single copy.
Component variants and states — default | secondary | destructive | outline — are shared vocabulary. When an enum or payload maps a status to one of these names, data and UI stay in sync without translation layers.
Browse the showcase to see which states exist before inventing new ones in an API.
Foundations
A beige neutral scale with a green brand primary. Semantic tokens are the API; raw scales are the material they're built from.
Defined in globals.css → :root / .dark. Values resolve through the raw scales — hover a value to see the declaration as written.
basePage canvas & default text
--backgroundoklch(0.9512 0.0121 96.4253)--foregroundoklch(0.2435 0 0)cardElevated surfaces
--cardoklch(1 0 0)--card-foregroundoklch(0.2435 0 0)popoverFloating surfaces
--popoveroklch(1 0 0)--popover-foregroundoklch(0.2435 0 0)primaryBrand green — main actions
--primaryoklch(0.5904 0.1251 139.9147)--primary-foregroundoklch(0.9855 0.0174 131.869)secondaryBrand orange — supporting
--secondaryoklch(0.6933 0.2058 42.8784)--secondary-foregroundoklch(0.9846 0.0124 75.3692)mutedSubdued surfaces & text
--mutedoklch(0.9725 0.0067 97.3514)--muted-foregroundoklch(0.5328 0.0463 96.9201)accentHover & selection
--accentoklch(0.9725 0.0067 97.3514)--accent-foregroundoklch(0.359 0.0344 96.3589)destructiveErrors & irreversible actions
--destructiveoklch(0.577 0.245 27.325)border / input / ringHairlines, fields & focus
--borderoklch(0.8657 0.0249 96.6167)--inputoklch(0.8657 0.0249 96.6167)--ringoklch(0.7803 0.0376 96.8081)chartsData visualization ramp
--chart-1oklch(0.7803 0.0589 312.8)--chart-2oklch(0.6254 0.1002 313)--chart-3oklch(0.5062 0.1307 313.4)--chart-4oklch(0.4505 0.142 311.7)--chart-5oklch(0.4004 0.1307 311.3)sidebarSidebar surfaces & accents
--sidebaroklch(0.985 0.0041 91.4457)--sidebar-foregroundoklch(0.2435 0 0)--sidebar-primaryoklch(0.5241 0.107 140.7839)--sidebar-primary-foregroundoklch(0.9855 0.0174 131.869)--sidebar-accentoklch(0.9725 0.0067 97.3514)--sidebar-accent-foregroundoklch(0.359 0.0344 96.3589)--sidebar-borderoklch(0.9512 0.0121 96.4253)--sidebar-ringoklch(0.7803 0.0376 96.8081)greenBrand primary
--color-green-50oklch(0.9855 0.0174 131.869)--color-green-100oklch(0.9541 0.0347 129.4029)--color-green-200oklch(0.8959 0.0613 131.4145)--color-green-300oklch(0.811 0.1053 135.5721)--color-green-400oklch(0.6933 0.1222 138.0136)--color-green-500oklch(0.5904 0.1251 139.9147)--color-green-600oklch(0.5241 0.107 140.7839)--color-green-700oklch(0.4581 0.0844 140.6847)--color-green-800oklch(0.3761 0.0672 140.2235)--color-green-900oklch(0.3299 0.0536 141.3419)--color-green-950oklch(0.2233 0.0367 141.7116)beigeNeutral scale
--color-beige-50oklch(0.985 0.0041 91.4457)--color-beige-100oklch(0.9725 0.0067 97.3514)--color-beige-200oklch(0.9512 0.0121 96.4253)--color-beige-300oklch(0.8657 0.0249 96.6167)--color-beige-400oklch(0.7803 0.0376 96.8081)--color-beige-500oklch(0.7036 0.0535 96.335)--color-beige-600oklch(0.6182 0.0499 96.6275)--color-beige-700oklch(0.5328 0.0463 96.9201)--color-beige-800oklch(0.4254 0.0392 98.1981)--color-beige-900oklch(0.359 0.0344 96.3589)--color-beige-950oklch(0.2435 0 0)orangeBrand secondary
--color-orange-50oklch(0.9846 0.0124 75.3692)--color-orange-100oklch(0.957 0.034 77.213)--color-orange-200oklch(0.9009 0.0713 71.648)--color-orange-300oklch(0.8331 0.1223 66.1577)--color-orange-400oklch(0.741 0.1763 53.256)--color-orange-500oklch(0.6933 0.2058 42.8784)--color-orange-600oklch(0.6353 0.2145 37.0344)--color-orange-700oklch(0.5438 0.1884 34.5898)--color-orange-800oklch(0.4622 0.1517 33.6008)--color-orange-900oklch(0.4012 0.1188 34.3827)--color-orange-950oklch(0.2616 0.0763 32.6596)purpleCharts & data viz
--color-purple-50oklch(0.9858 0.0073 312.3)--color-purple-100oklch(0.9433 0.0166 312.6)--color-purple-200oklch(0.883 0.0314 312.7)--color-purple-300oklch(0.7803 0.0589 312.8)--color-purple-400oklch(0.6254 0.1002 313)--color-purple-500oklch(0.5062 0.1307 313.4)--color-purple-600oklch(0.4505 0.142 311.7)--color-purple-700oklch(0.4004 0.1307 311.3)--color-purple-800oklch(0.3536 0.1075 313.2)--color-purple-900oklch(0.3076 0.0868 314.5)--color-purple-950oklch(0.2349 0.0735 312.2)redErrors & destructive
--color-red-50oklch(0.971 0.013 17.38)--color-red-100oklch(0.936 0.032 17.717)--color-red-200oklch(0.885 0.062 18.334)--color-red-300oklch(0.808 0.114 19.571)--color-red-400oklch(0.704 0.191 22.216)--color-red-500oklch(0.637 0.237 25.331)--color-red-600oklch(0.577 0.245 27.325)--color-red-700oklch(0.505 0.213 27.518)--color-red-800oklch(0.444 0.177 26.899)--color-red-900oklch(0.396 0.141 25.723)--color-red-950oklch(0.258 0.092 26.042)Each scale runs 50 → 950, parsed straight from the tokens in globals.css. Components never reference raw steps directly — they use the semantic tokens above, which remap between light and dark automatically.
Foundations
Three faces, three jobs — display, body and mono.
Money that moves with you
Headings and display text. The font ships in one weight only — always pair font-heading with font-medium, never semibold or bold.
300The quick brown fox jumps over the lazy dog
400The quick brown fox jumps over the lazy dog
500The quick brown fox jumps over the lazy dog
600The quick brown fox jumps over the lazy dog
700The quick brown fox jumps over the lazy dog
Body copy, labels and UI text. Variable font, weights 300–900.
ACC-2049-8817 · $12,480.00
const balance = formatCurrency(12480)
Code, identifiers and tabular figures — anywhere digits need to line up.
Foundations
The remaining primitives every screen inherits.
sm
md
lg
xl
2xl
One knob: --radius (0.625rem). Every size derives from it, so changing the knob reshapes the whole system consistently.
Phosphor is the only icon library — never lucide. Icons inherit the current text color, so they follow the tokens for free.
Class-based via next-themes — the .dark class remaps every token, so components never branch on theme. Press D to try it.
A Yarn workspaces + Turborepo monorepo: the design system is a source-shipped package, consumers transpile it.
packages/ui
@workspace/ui — tokens, 60 components, hooks. Source in the monorepo; compiled ESM and types on the release branch.
workspace / release
apps/web
This showcase — every component demoed under the shared tokens.
WestyApp
The product — next consumer, same workspace import.
| Subpath | What it is | Example import |
|---|---|---|
| ./components/* | Every component, one module each | @workspace/ui/components/button |
| ./hooks/* | Shared hooks | @workspace/ui/hooks/use-mobile |
| ./lib/* | Utilities (cn) | @workspace/ui/lib/utils |
| ./globals.css | All tokens + Tailwind v4 config | @workspace/ui/globals.css |
| ./postcss.config | The single PostCSS pipeline | @workspace/ui/postcss.config |
There is no barrel file — import the exact module you need. The generated release resolves this same surface to compiled files under dist/. Anything not on this surface is internal and may change without notice.
The five rules that keep the system coherent as it grows.
Components only reference semantic classes — bg-background, text-muted-foreground, border-border. A color that isn't a token doesn't ship.
Components export families of parts (Field, FieldLabel, FieldDescription…) you compose, instead of piling boolean props onto one component. asChild is the escape hatch for changing the rendered element.
Components track the upstream output so the CLI can add and update them cleanly. Intent lives in tokens and variants, not hand-edited class strings.
If it isn't in the showcase, it's invisible and untested. The showcase is the review surface for every change.
react, react-dom and next-themes are peer dependencies — the consuming app holds the single copy, so hooks and theme context never split across duplicates.
All 60 components, grouped into ten categories. Each card jumps to its live demos.
Typography
Type scale, prose and inline text primitives
Buttons
Buttons, button groups and toggles
Forms
Inputs, fields, input groups and validation states
Selection
Checkboxes, radios, selects, sliders and switches
Overlays
Dialogs, sheets, drawers, menus and popovers
Feedback
Alerts, toasts, progress, skeletons and empty states
Data display
Tables, cards, avatars, accordions and badges
Navigation
Tabs, breadcrumbs, pagination, sidebar and menubar
Charts
Recharts primitives themed by the purple ramp
Chat
Messages, bubbles, attachments and scrollers