Status: complete. All five phases (0–4) are implemented and archived. The full 60-component catalog is registered in packages/patties-ui/src/registry.ts with status: "completed" and has a stamped template under packages/patties-ui/templates/. This overview is retained as the catalog reference; the per-component specs live alongside it under phase-0/phase-4/.

Mission

Rebuild the full shadcn/ui catalog as Patties-native components: copy-in source (no runtime package), Bun-buildable, edge-safe, and SSR-first with explicit client-island boundaries.

This is not a fork of shadcn/ui. It is a parallel catalog with the same API surface and visual design, retuned for Patties.

Catalog scope. Upstream shadcn (per llms.txt, fetched 2026-05-24) lists 59 components and no longer ships Form — it has been superseded by Field (see 26-field). Patties keeps a 27-form spec for users migrating from the older react-hook-form recipe, marked compat in the index below.

The retuning:

  1. Static parts render on the server with zero client JS by default.
  2. Interactive parts are marked as Patties islands (see framework/06-client-islands) and hydrate independently.
  3. All dependencies are Bun-compatible and WinterCG-safe (no Node-only built-ins, no process access at module top-level).
  4. Source is delivered by the Patties CLI (bun patties add <component>) into app/components/ui/<name>.tsx — never imported from a versioned npm package.

Pillars

  • Copy-in, not install. Components live in the user's repo. They can edit freely. The CLI just stamps source files and patches package.json peer deps.
  • SSR by default, island by opt-in. Every component file declares an island: true | false | "subtree" export so the build can decide whether to ship the component to the client. "subtree" means the component itself is static but inherits an island root from its parent (see Button). Components with state, refs to document, or event handlers default to island: true.
  • Tailwind v4 + CSS variables. Same theming model as upstream shadcn — --background, --foreground, --primary, etc. — wired through @theme inline in app.css.
  • Radix-class primitives, vendor-neutral. We adopt @radix-ui/* headless primitives where shadcn does. Each spec documents the exact peer-dep set; the CLI installs them on demand.
  • JSON-serializable props at island boundaries. Per framework spec 06, callbacks crossing the SSR→island boundary are forbidden. Each component spec calls out which props must be local to the island.
  • No forwardRef boilerplate. Patties targets React 19+; refs are passed as plain props. Component specs use the React-19 style.

Delivery layout

app/components/ui/
  accordion.tsx
  alert.tsx
  …
app/components/ui/_internal/    # shared helpers (cn, slot, variants)
app/styles/tokens.css           # CSS variables — generated by CLI

_internal/cn.ts depends on clsx and tailwind-merge; the CLI installs both the first time any component is added.

Default icon set. Components that ship an icon (chevrons, checks, close, ellipsis) use lucide-react for visual parity with upstream shadcn. Userland can swap icons by editing the stamped source — no central icon registry. Specs list lucide-react only when the file references it directly.

The CLI command (see cli/draft):

bun patties add <component> [...components]
bun patties add --all

stamps:

  1. The component source file(s) from this spec catalog.
  2. Any missing helpers into _internal/.
  3. Any missing peer deps (@radix-ui/react-*, class-variance-authority, tailwind-merge, lucide-react) into package.json.
  4. Token CSS (idempotent merge into app/styles/tokens.css).

Spec template

Each component spec is one file. Required sections:

  • frontmatterspec, title, status, island, peer_deps, last_reviewed.
  • ## Purpose — one paragraph, what it is and what shadcn component it mirrors.
  • ## Island modelisland: true | false | "subtree", with reasoning. ("subtree" = a static outer shell with one nested island, e.g. a Card that wraps an interactive button.)
  • ## Peer dependencies — exact npm package names and version constraints.
  • ## Public API — the exported components and their props (TS-shape; no narration).
  • ## Patties adjustments — what differs from upstream shadcn: removed forwardRef, added island export, replaced useTheme hook, etc.
  • ## Acceptance criteria — concrete, testable bullets.

Phase plan

The catalog was staged into five phases, each its own folder, picked up in numeric order within a phase. All five are now implemented and archived under agent_specs/ui/archive/.

PhaseThemeScope
phase-0Foundationbun patties add CLI command, _internal/ helpers, tokens.css merge, peer-dep patcher, island export convention, snapshot + hydration test harness. No components.
phase-1Zero-JS primitives (island: no)18 components — Alert, Aspect Ratio, Badge, Breadcrumb, Button Group, Card, Direction, Empty, Input Group, Item, Kbd, Label, Pagination, Separator, Skeleton, Spinner, Table, Typography.
phase-2Form primitives (island: yes ⇣) + Button subtree12 components — Button, Checkbox, Field, Form (compat), Input, Native Select, Progress, Radio Group, Slider, Switch, Textarea, Toggle. Proves the SSR-first / island-on-opt-in path end-to-end.
phase-3Radix-backed islands21 components — Accordion, Alert Dialog, Avatar, Collapsible, Context Menu, Dialog, Drawer, Dropdown Menu, Hover Card, Input OTP, Menubar, Navigation Menu, Popover, Scroll Area, Select, Sheet, Sonner, Tabs, Toast, Toggle Group, Tooltip.
phase-4Recipes & heavy islands9 components — Calendar, Carousel, Chart, Combobox, Command, Data Table, Date Picker, Resizable, Sidebar.

A component's spec lives in exactly one phase folder. All phase folders now sit under archive/; the index below links to each spec's location.

Component index

Kind: primitive = thin wrapper around a single Radix/headless library; recipe = composed multi-piece pattern stamped as editable source; provider = React context only. Island: no = zero JS; yes = hydrates; subtree = static, inherits parent island; yes ⇣ = hydrates by default but downgrades to zero JS in the documented nativeForm / uncontrolled path.

#ComponentKindIslandshadcn parity
01Accordionprimitiveyesfull
02Alertprimitivenofull
03Alert Dialogprimitiveyesfull
04Aspect Ratioprimitivenofull
05Avatarprimitiveyesfull
06Badgeprimitivenofull
07Breadcrumbprimitivenofull
08Buttonprimitivesubtreefull
09Button Groupprimitivenofull
10Calendarprimitiveyesfull
11Cardprimitivenofull
12Carouselprimitiveyesfull
13Chartprimitiveyesfull
14Checkboxprimitiveyes ⇣full
15Collapsibleprimitiveyesfull
16Comboboxrecipeyesfull
17Commandprimitiveyesfull
18Context Menuprimitiveyesfull
19Data Tablerecipeyesfull
20Date Pickerrecipeyesfull
21Dialogprimitiveyesfull
22Directionprovidernofull
23Drawerprimitiveyesfull
24Dropdown Menuprimitiveyesfull
25Emptyprimitivenofull
26Fieldprimitiveyesfull
27Formrecipeyescompat (dropped upstream in favor of Field)
28Hover Cardprimitiveyesfull
29Inputprimitiveyes ⇣full
30Input Groupprimitivenofull
31Input OTPprimitiveyesfull
32Itemprimitivenofull
33Kbdprimitivenofull
34Labelprimitivenofull
35Menubarprimitiveyesfull
36Native Selectprimitiveyes ⇣full
37Navigation Menuprimitiveyesfull
38Paginationprimitivenofull
39Popoverprimitiveyesfull
40Progressprimitiveyes ⇣full
41Radio Groupprimitiveyes ⇣full
42Resizableprimitiveyesfull
43Scroll Areaprimitiveyesfull
44Selectprimitiveyesfull
45Separatorprimitivenofull
46Sheetprimitiveyesfull
47Sidebarrecipeyesfull
48Skeletonprimitivenofull
49Sliderprimitiveyes ⇣full
50Sonnerprimitiveyesfull
51Spinnerprimitivenofull
52Switchprimitiveyes ⇣full
53Tableprimitivenofull
54Tabsprimitiveyesfull
55Textareaprimitiveyes ⇣full
56Toastprimitiveyessuperseded by Sonner; kept for parity
57Toggleprimitiveyes ⇣full
58Toggle Groupprimitiveyesfull
59Tooltipprimitiveyesfull
60Typographyprimitivenofull

Cross-cutting acceptance criteria

These apply to every component below. Component-level acceptance criteria assume these as a baseline:

  • The component file exports a constant island: boolean | "subtree" for the build pipeline.
  • The component file passes bun build --target=browser with no Node-builtin warnings.
  • Non-JSON props (functions, Dates, Maps, Sets, BigInt) at an island root are flagged by the build per framework/06-client-islands. Runtime enforcement is a follow-up against that spec.
  • Tailwind classes resolve under Tailwind v4 with the Patties default tokens.css.
  • Dark mode flips by toggling class="dark" on <html>; no JS theming dependency.
  • No top-level access to window, document, process, or globalThis.navigator. All such access is inside event handlers or useEffect.
  • bun test runs a snapshot of the static SSR HTML and a hydration round-trip test for any island component.