Getting started

Introduction

Five tiers of Woohl components, installed with the shadcn CLI.

The Woohl design system is distributed as a shadcn registry. You install a component with one command and it lands in your repo as source you own — no package to version, no build step, nothing to keep in sync by hand.

bunx --bun shadcn@latest add @woohl/button

Why a registry rather than a package

Four apps share this system: the buyer storefront, the seller portal, the admin console, and this site. They are separate repositories on separate release cadences, and for a long time the system was propagated between them by copying a folder. That worked until the copies drifted — one repo fixed a bug locally, the next copy overwrote it, and the diff that was supposed to catch that had become too noisy to read.

A registry keeps the good part of copying (you own the source, you can edit it, nothing is hidden behind a version range) and removes the bad part: the CLI resolves dependencies, rewrites import paths, and installs the npm packages a component needs.

The five tiers

Each tier may depend on the ones above it and never the other way round.

TierWhat lives there
Tokens & brandColour, type, spacing, motion, the four product themes, the logo
Primitivescn, cva, structural glyphs, IconSlot, Separator, Skeleton
ComponentsButton, Input, Badge, Avatar, Switch, Checkbox, Radio, Tooltip, Field, Card
CompositesDialog, Drawer, Dropdown, Popover, Select, Tabs, Table, Pagination, Toast, Navigation
BlocksProductCard, SellerCard, CartItem, Price, Rating and the rest of the marketplace UI

Installing a block pulls its whole chain. @woohl/product-card brings the tokens, cn, Badge, Price, Rating and WishlistButton with it, because the CLI follows registryDependencies for you.

Icons are yours, not ours

Nothing in this system ships an icon set. The buyer app draws Phosphor, the seller and admin portals draw Lucide, and a shared component that picked one would be wrong in two places out of three.

Components that need a mark you chose take it as a prop:

<Button icon={<Icon name="search" />}>Search</Button>

The handful of marks a control draws as part of itself — a checkbox's tick, a dialog's close cross, a select's caret — come from @woohl/glyphs and are not an icon set you should reach into for anything else.

On this page