shadcn compatibility
One library, compatible by contract: shadcn's token names, shadcn's idioms, your components/ui untouched.
Compatibility is a contract, not a copy
Refined UI does not ship a parallel "shadcn version" of itself. ONE library ships, and it stays compatible with shadcn the
way a good citizen does: it speaks shadcn's token language, keeps shadcn's component idioms, installs through the same
npx shadcn add workflow, and lives beside your existing components/ui/* without ever writing to it.
npx shadcn@latest add <registry-url>/r/refined/<item>.json - and the installed files are yours to edit.cn() merging, caller className always winning, data-slot anatomy, variant props - shadcn muscle memory transfers.components/refined-ui/*; your components/ui/* stays untouched and both coexist in one app.Bring your shadcn theme
The token contract's core rung IS shadcn's theme vocabulary: --background, --foreground, --card, --popover,
--primary, --secondary, --muted, --accent, --destructive, --border, --input, --ring (plus their
-foreground pairs). Paste the :root / .dark block you already have and the library wears your brand directly.
/* your existing shadcn theme keeps working as-is */:root { --background: oklch(1 0 0); --foreground: oklch(0.145 0 0); --primary: oklch(0.6131 0.2067 283.83); --primary-foreground: oklch(1 0 0); --radius: 0.625rem; /* … the rest of your shadcn variables … */}.dark { --background: oklch(0.145 0 0); --foreground: oklch(0.985 0 0); /* … */}:where(:root)), so any value your app writes wins - no !important, no ordering games.--text-body, --text-label), a parametric shadow recipe, motion durations, per-surface radii. All have baseline defaults; adopt only what you brand.Your app's theme: :root, not a pack
The skin packs in this registry scope their tokens to [data-skin="<id>"]. That is the DISTRIBUTION format - it lets
ten packs compile in one Tailwind build (this docs site does exactly that) and lets portalled popups re-assert their scope.
Your app does not need it: one brand, worn forever, is a plain :root / .dark block - the shadcn convention above.
globals.css under :root / .dark. Done - no pack, no attribute, no name.:root / .dark, plus skin.config slot overrides when you restyle what recipes paint - the config is a static module the components already read.data-skin="<id>" once on <html>. The same artifact then works in any app, the theme editor can hydrate from it, and several skins can coexist.shadcn-shaped APIs on Base UI
Components are Base UI under the hood with a deliberate shadcn facade: TypeScript namespaces flattened to named exports,
data-state attributes where shadcn consumers expect them, ComponentProps-typed parts, and compound anatomy instead of
prop bags. If you can compose a shadcn Dialog, you can compose these.
cn(recipe, skin override, your className) - your class is always last and always final.data-ui="agent", data-slot, data-control, data-variant, data-tone, data-size - stable hooks for CSS, tests, and extensions.Migrating a shadcn app
There is no big-bang migration: both trees coexist, so adopt surface by surface.
components/ui/* for everything that already works; nothing in Refined UI depends on it or overwrites it.chat, tool-call, blocks) - they bring their own slots under components/refined-ui/ui/*.@/components/ui/x to @/components/refined-ui/ui/x at the call sites you choose - the props read the same.:root story), so the new surfaces match your brand from the first render.