shadcn/ui
This skill keeps shadcn/ui work project-aware. Components are source files in the app, so always inspect the local project before adding, importing, or rewriting them.
First Steps
- Work from the app root that owns
components.json.
- In an Agent-Native app, inspect
app/design-system.ts and
ToolkitProvider before choosing a primitive. A registered company design
system takes precedence over the default shadcn adapter.
- Run
pnpm dlx shadcn@latest info --json when you need current project context: framework, Tailwind version, aliases, icon library, installed components, and resolved paths.
- Use the actual aliases from
components.json or shadcn info; do not assume @/components/ui if the project says otherwise.
- Check
app/components/ui/ or the resolved ui path before importing a component.
- For unfamiliar components, run
pnpm dlx shadcn@latest docs <component> and read the returned docs or examples before coding.
Agent-Native Adapter Rule
Pages, routes, and domain components import controls through the app's local UI
adapter layer. Never import @agent-native/toolkit/ui/* directly in app product
code. Direct imports bypass app/design-system.ts and make Toolkit/Core
surfaces use different controls from the app.
When shadcn is the app's adapter, add or update the local primitive and keep
product code on that local import. When a company design system is registered,
adapt its components to the semantic contracts from
@agent-native/toolkit/design-system; do not recreate a parallel shadcn
surface. The semantic API is styling-runtime agnostic, so do not require
Tailwind, CVA, or className in customer adapters.
For shared Toolkit features, customize presentation through semantic
components, a feature-level controller, and product-level render slots. Keep
the same controller for default and custom views. Use the conformance kit for
behavior components whose focus, portal, keyboard, dismissal, or stacking
behavior comes from the company design system.
Adding Or Updating Components
- Add missing primitives with
pnpm dlx shadcn@latest add <component> from the app root.
- Before overwriting an existing component, use
pnpm dlx shadcn@latest add <component> --dry-run and --diff to inspect the change.
- After adding registry code, read the generated files. Fix import aliases, icon imports, missing subcomponents, and composition issues before using the component.
- Do not fetch raw component files manually from GitHub when the shadcn CLI can resolve the registry item.
- If a user asks to add a third-party block but does not name a registry, ask which registry to use instead of guessing.
Component Composition
- Use existing primitives before custom markup:
Alert for callouts, Badge for small status labels, Separator for dividers, Skeleton for placeholders, Table for tabular data, and Card for framed content.
- Compose cards from
CardHeader, CardTitle, CardContent, and CardFooter. Leave CardDescription out: a card gets a title or a description, never both. See frontend-design → Default Surface Density.
- Dialog, Sheet, Drawer, and AlertDialog content must include an accessible title. Use visually hidden titles only when the visible UI already communicates the title.
- Put items inside their group components:
SelectItem in SelectGroup, DropdownMenuItem in DropdownMenuGroup, CommandItem in CommandGroup, and equivalent menu groups.
TabsTrigger belongs inside TabsList.
Avatar always needs AvatarFallback.
- Buttons do not have magic loading props. Compose loading with
disabled, Spinner, and clear text.
Forms And Inputs
- Use the app's shadcn form primitives instead of raw div stacks.
- If
Field, FieldGroup, FieldSet, or InputGroup are installed or worth adding, use them for form layout, grouped fields, and input add-ons.
- Do not place buttons inside inputs with absolute positioning. Use
InputGroup and InputGroupAddon when available.
- Use
ToggleGroup for small option sets, RadioGroup for one-of-many choices, Checkbox for multi-select, Switch for settings toggles, Select or Combobox for predefined choices, and Slider or numeric input for numeric values.
- Validation must be accessible: pair visual invalid states with
aria-invalid, and connect descriptions/errors to controls.
Styling And Theming
- Use semantic tokens (
bg-background, text-foreground, text-muted-foreground, bg-primary, border-border, text-destructive) instead of raw colors for reusable app UI.
- Prefer built-in variants and sizes before custom classes.
- Use
className mostly for layout and spacing; avoid overriding component colors and typography unless the component is intentionally being extended.
- Use
gap-* instead of space-x-* / space-y-*.
- Use
size-* when width and height are equal.
- Use
truncate for single-line clipping.
- Use
cn() for conditional classes.
- Do not add manual
z-index to overlay primitives unless you are fixing a verified stacking bug.
- Add custom colors as CSS variables in the existing Tailwind CSS file reported by shadcn info. For Tailwind v4, register variables with
@theme inline.
Transitions And Motion
shadcn's built-in component animations are the right level of polish — keep them. The goal is a snappy, clean UI, not a motionless one. Match shadcn's motion vocabulary; don't strip it and don't pile on decorative custom animation.
- Never remove or override a shadcn component's default animation.
data-[state=open]:animate-in, data-[state=closed]:animate-out, fade-in/out, zoom-in/out, slide-in-from-*, accordion height, the tailwindcss-animate utilities — these ship for a reason. Leave them as-is.
- Custom transitions are fine when they communicate a state change and match shadcn's feel. Reuse the same vocabulary: short durations (~120–200ms),
ease-out, opacity/transform only, gated on data-[state=...]. Examples that are good and welcome:
- A portaled custom popover/tooltip/sheet that fades + scales/slides in on
data-[state=delayed-open] / data-[state=closed], mirroring Radix's own content animation.
- A list row or toast that fades/slides in on mount and out on dismiss.
- A chevron/caret
rotate on expand, a subtle opacity/color hover on an icon button, skeleton shimmer, a progress/height transition on a collapsible.
- Continuous, product-defining motion where it is the experience (e.g. a multi-stage booking flow's stage transitions) — fine, and framer-motion is acceptable there.
- Avoid decorative, attention-seeking, or slow motion: hand-rolled
duration-700 hero fade-ins, parallax, bouncing/spring entrances on ordinary content, animated gradients, staggered cascades on long lists, anything that delays the user seeing or acting on content. If an animation makes the UI feel slower, cut it.
- Rule of thumb: if the motion clarifies what just changed and is over in well under a quarter-second, it's polish; if it's there to look impressive, it's bloat.
Icons
- Agent-Native apps use
@tabler/icons-react. Do not add lucide-react because a registry example used it.
- If registry code imports a different icon package, replace those imports with Tabler equivalents before finishing.
- Let shadcn components size icons through their CSS. Avoid manual icon sizing inside buttons, menus, alerts, and sidebars unless the local component API requires it.
Base-Specific APIs
Check the project context before using trigger composition APIs:
- Radix-based components use
asChild for custom triggers.
- Base UI components may use
render and sometimes nativeButton={false}.
Do not wrap triggers in extra divs just to place a Button or Link inside them.
Related Skills
- frontend-design — Product UX, visual direction, responsive polish, and verification
- customizing-agent-native — Registered design systems, controllers, slots, conformance, and ejection
- actions — Data fetching and mutation patterns for agent-native apps
- security — User data, forms, external input, and action safety
1---2name: shadcn-ui3description: The shadcn CLI, component composition, theming, and registry workflow. Use when adding, replacing, upgrading, or debugging a shadcn/ui primitive itself, or when changing a theme or registry. Do not load it for ordinary edits to a file that happens to import a shadcn component.4---56# shadcn/ui78This skill keeps shadcn/ui work project-aware. Components are source files in the app, so always inspect the local project before adding, importing, or rewriting them.910## First Steps11121. Work from the app root that owns `components.json`.132. In an Agent-Native app, inspect `app/design-system.ts` and14 `ToolkitProvider` before choosing a primitive. A registered company design15 system takes precedence over the default shadcn adapter.163. Run `pnpm dlx shadcn@latest info --json` when you need current project context: framework, Tailwind version, aliases, icon library, installed components, and resolved paths.174. Use the actual aliases from `components.json` or `shadcn info`; do not assume `@/components/ui` if the project says otherwise.185. Check `app/components/ui/` or the resolved `ui` path before importing a component.196. For unfamiliar components, run `pnpm dlx shadcn@latest docs <component>` and read the returned docs or examples before coding.2021## Agent-Native Adapter Rule2223Pages, routes, and domain components import controls through the app's local UI24adapter layer. Never import `@agent-native/toolkit/ui/*` directly in app product25code. Direct imports bypass `app/design-system.ts` and make Toolkit/Core26surfaces use different controls from the app.2728When shadcn is the app's adapter, add or update the local primitive and keep29product code on that local import. When a company design system is registered,30adapt its components to the semantic contracts from31`@agent-native/toolkit/design-system`; do not recreate a parallel shadcn32surface. The semantic API is styling-runtime agnostic, so do not require33Tailwind, CVA, or `className` in customer adapters.3435For shared Toolkit features, customize presentation through semantic36components, a feature-level controller, and product-level render slots. Keep37the same controller for default and custom views. Use the conformance kit for38behavior components whose focus, portal, keyboard, dismissal, or stacking39behavior comes from the company design system.4041## Adding Or Updating Components4243- Add missing primitives with `pnpm dlx shadcn@latest add <component>` from the app root.44- Before overwriting an existing component, use `pnpm dlx shadcn@latest add <component> --dry-run` and `--diff` to inspect the change.45- After adding registry code, read the generated files. Fix import aliases, icon imports, missing subcomponents, and composition issues before using the component.46- Do not fetch raw component files manually from GitHub when the shadcn CLI can resolve the registry item.47- If a user asks to add a third-party block but does not name a registry, ask which registry to use instead of guessing.4849## Component Composition5051- Use existing primitives before custom markup: `Alert` for callouts, `Badge` for small status labels, `Separator` for dividers, `Skeleton` for placeholders, `Table` for tabular data, and `Card` for framed content.52- Compose cards from `CardHeader`, `CardTitle`, `CardContent`, and `CardFooter`. Leave `CardDescription` out: a card gets a title or a description, never both. See `frontend-design` → Default Surface Density.53- Dialog, Sheet, Drawer, and AlertDialog content must include an accessible title. Use visually hidden titles only when the visible UI already communicates the title.54- Put items inside their group components: `SelectItem` in `SelectGroup`, `DropdownMenuItem` in `DropdownMenuGroup`, `CommandItem` in `CommandGroup`, and equivalent menu groups.55- `TabsTrigger` belongs inside `TabsList`.56- `Avatar` always needs `AvatarFallback`.57- Buttons do not have magic loading props. Compose loading with `disabled`, `Spinner`, and clear text.5859## Forms And Inputs6061- Use the app's shadcn form primitives instead of raw div stacks.62- If `Field`, `FieldGroup`, `FieldSet`, or `InputGroup` are installed or worth adding, use them for form layout, grouped fields, and input add-ons.63- Do not place buttons inside inputs with absolute positioning. Use `InputGroup` and `InputGroupAddon` when available.64- Use `ToggleGroup` for small option sets, `RadioGroup` for one-of-many choices, `Checkbox` for multi-select, `Switch` for settings toggles, `Select` or `Combobox` for predefined choices, and `Slider` or numeric input for numeric values.65- Validation must be accessible: pair visual invalid states with `aria-invalid`, and connect descriptions/errors to controls.6667## Styling And Theming6869- Use semantic tokens (`bg-background`, `text-foreground`, `text-muted-foreground`, `bg-primary`, `border-border`, `text-destructive`) instead of raw colors for reusable app UI.70- Prefer built-in variants and sizes before custom classes.71- Use `className` mostly for layout and spacing; avoid overriding component colors and typography unless the component is intentionally being extended.72- Use `gap-*` instead of `space-x-*` / `space-y-*`.73- Use `size-*` when width and height are equal.74- Use `truncate` for single-line clipping.75- Use `cn()` for conditional classes.76- Do not add manual `z-index` to overlay primitives unless you are fixing a verified stacking bug.77- Add custom colors as CSS variables in the existing Tailwind CSS file reported by shadcn info. For Tailwind v4, register variables with `@theme inline`.7879## Transitions And Motion8081shadcn's built-in component animations are the right level of polish — keep them. The goal is a snappy, clean UI, not a motionless one. Match shadcn's motion vocabulary; don't strip it and don't pile on decorative custom animation.8283- **Never remove or override a shadcn component's default animation.** `data-[state=open]:animate-in`, `data-[state=closed]:animate-out`, `fade-in/out`, `zoom-in/out`, `slide-in-from-*`, accordion height, the `tailwindcss-animate` utilities — these ship for a reason. Leave them as-is.84- **Custom transitions are fine when they communicate a state change and match shadcn's feel.** Reuse the same vocabulary: short durations (~120–200ms), `ease-out`, opacity/transform only, gated on `data-[state=...]`. Examples that are good and welcome:85 - A portaled custom popover/tooltip/sheet that fades + scales/slides in on `data-[state=delayed-open]` / `data-[state=closed]`, mirroring Radix's own content animation.86 - A list row or toast that fades/slides in on mount and out on dismiss.87 - A chevron/caret `rotate` on expand, a subtle `opacity`/`color` hover on an icon button, skeleton shimmer, a progress/height transition on a collapsible.88 - Continuous, product-defining motion where it _is_ the experience (e.g. a multi-stage booking flow's stage transitions) — fine, and framer-motion is acceptable there.89- **Avoid decorative, attention-seeking, or slow motion:** hand-rolled `duration-700` hero fade-ins, parallax, bouncing/spring entrances on ordinary content, animated gradients, staggered cascades on long lists, anything that delays the user seeing or acting on content. If an animation makes the UI feel slower, cut it.90- Rule of thumb: if the motion clarifies what just changed and is over in well under a quarter-second, it's polish; if it's there to look impressive, it's bloat.9192## Icons9394- Agent-Native apps use `@tabler/icons-react`. Do not add `lucide-react` because a registry example used it.95- If registry code imports a different icon package, replace those imports with Tabler equivalents before finishing.96- Let shadcn components size icons through their CSS. Avoid manual icon sizing inside buttons, menus, alerts, and sidebars unless the local component API requires it.9798## Base-Specific APIs99100Check the project context before using trigger composition APIs:101102- Radix-based components use `asChild` for custom triggers.103- Base UI components may use `render` and sometimes `nativeButton={false}`.104105Do not wrap triggers in extra divs just to place a Button or Link inside them.106107## Related Skills108109- **frontend-design** — Product UX, visual direction, responsive polish, and verification110- **customizing-agent-native** — Registered design systems, controllers, slots, conformance, and ejection111- **actions** — Data fetching and mutation patterns for agent-native apps112- **security** — User data, forms, external input, and action safety