Tailwind + shadcn/ui
Apply these patterns whether you're building new UI from scratch or refactoring existing code — the rules are the same either way.
Detect framework first
This skill serves two ecosystems. Before doing anything, read
to detect which one you're in:
- / present → React mode. Primitive layer is Base UI; the CLI is the official . Gather project context by running:
json
npx shadcn@latest info --json
Key fields: (v4 uses ; v3 uses ), (always edit this file for CSS variables — never create a new one), (use the actual alias prefix for imports, never hardcode), (the primitive layer).
- present → Solid mode. Primitive layer is Kobalte (Zaidan components); the CLI is (much thinner — no //). Gather project context by reading + . See references/cli-shadcn-solid.md and references/primitives-solid.md.
Framework mode
| Concern | React mode | Solid mode |
|---|
| Primitive layer | Base UI — see "Primitive layer APIs" below | Kobalte (Zaidan) — see references/primitives-solid.md |
| CLI | — see references/cli.md | — see references/cli-shadcn-solid.md |
| JSX class attribute | | |
| Component composition | + | / Kobalte polymorphic |
| Lists | | / |
Normalization note (Solid mode): the styling discipline below and the shared references (
,
,
) use React (
) syntax in their examples. In Solid mode,
read every as , read
+
as
, and ignore React-only constructs (
types,
,
). See
references/primitives-solid.md for the Solid equivalents. The Tailwind/token discipline itself is identical in both modes.
Principles
- Reusability first — exhaust existing options before writing any inline/custom component. Work down this ladder in order:
- Search what the project already has. Scan the UI components directory ( in React mode; the components dir in Solid mode) for an existing component that fits.
- Extend what exists. If an existing component nearly fits and it makes semantic sense, reuse it. Add a new variant only when the styling will be reused in more than one place — for a one-off, prefer composition or a override. Don't reach for a variant by default; overusing adds complexity. See references/cva.md.
- Bring in an available component. Check the registry before writing custom UI — (React); browse the shadcn-solid catalog (Solid, no ). Check community registries too.
- Only then write custom code, and consult the component docs first (
npx shadcn@latest docs <component>
in React mode) for the recommended reusable pattern.
- Compose, don't reinvent. Settings page = Tabs + Card + form controls. Dashboard = Sidebar + Card + Chart + Table.
- Use built-in variants before custom styles. , , etc.
- Use semantic colors. , — never raw values like .
Critical Rules
These rules are always enforced.
Colors
- Never use raw Tailwind palette values (, ) in application code.
- Never hardcode hex/rgb/oklch values inline in className strings.
- Use semantic tokens for all color decisions: , , .
- For status indicators with no matching token, use variants first. If a reusable semantic token is needed (e.g. ), add it to the theme.
Spacing & layout
- No or . Use + instead. → .
- when width = height. not .
- not
overflow-hidden text-ellipsis whitespace-nowrap
.
Component styling
- is for layout, not overrides. Acceptable: . Not acceptable:
bg-blue-100 text-blue-900 font-bold
.
- Built-in variants first. Exhaust , , before adding new ones.
- Add a new variant sparingly — only when a styling pattern repeats (used in more than one place) and no built-in fits. Edit the component source using . Not every component needs variants; a one-off doesn't warrant one. See references/cva.md.
Class merging
- for all conditional or merged classes. No
${isActive ? 'bg-primary' : 'bg-muted'}
in className.
- No manual on overlay components (Dialog, Sheet, Popover, Tooltip, Drawer). They manage their own stacking.
- No manual overrides. Semantic tokens handle theme switching via CSS variables.
CSS Architecture
Variables at
All color tokens are CSS custom properties on
in the global CSS file (
from project context). Never scatter variable definitions across component files.
css
:root {
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
/* ... see references/theming.md for the full token set */
}
OKLCH: oklch(lightness chroma hue)
— perceptually uniform, so equal chroma values look equally saturated across hues. Prefer it over HSL.
Map variables to Tailwind
With
Tailwind v4, register every CSS variable via
:
css
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
/* ... see references/theming.md for the full mapping */
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
}
Apply base styles
css
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}
Do not double-wrap: inside
blocks, use utility classes (
) or
directly — never
.
Adding Theme Tokens
Before reaching for an arbitrary value or a raw Tailwind color:
Need a color or spacing unit →
Does a semantic token cover it? → use it
Does a Tailwind default utility cover it? → use it
Is this a one-off pixel nudge? → arbitrary is fine: [3px]
Will this value appear in more than once? → add a token
See references/theming.md for the full custom color and spacing token workflow, naming conventions, and the complete token table.
Components & CLI
Run all CLI commands with the project's package runner (
/
/
) — from
in project context.
React mode
bash
npx shadcn@latest add button card dialog
npx shadcn@latest add @magicui/shimmer-button # from a community registry
Preview before installing with
/
— see
references/cli.md. Full registry surface available:
,
,
.
Solid mode
bash
npx shadcn-solid@latest add button card dialog
The shadcn-solid CLI is thin —
only , , and . There is
no ,
,
,
, or
/
flag. Browse component docs at zaidan.carere.dev / shadcn-solid.com in the browser instead, and preview changes via the
command or by reading the copy-paste source. See
references/cli-shadcn-solid.md.
After adding (either mode), always read the added files and verify correct composition: check for missing sub-components, incorrect nesting, and violations of the Critical Rules above. In Solid mode, also apply the bundle-weight check from references/primitives-solid.md — trim or hand-roll components that pull Kobalte for no a11y benefit.
Primitive layer APIs
React mode — Base UI. Key differences from Radix to keep in mind:
- Composition: use the prop, not .
<DialogTrigger render={<Button />}>Open</DialogTrigger>
- Select: requires an prop on the root; for placeholder is in the items array.
- ToggleGroup / Accordion: use boolean instead of ; is always an array.
- Slider: accepts a plain number for a single thumb (), not an array.
Solid mode — Kobalte. The primitive APIs, composition (
prop), and the bundle-weight discipline that governs
when to reach for a Kobalte primitive at all are covered in
references/primitives-solid.md. Read it before adding any interactive Solid component.
Workflow
- Detect framework & get project context — read (React vs Solid), then gather context per "Detect framework first" above (React:
npx shadcn@latest info --json
; Solid: read + ).
- Check installed components first — before running , check what's already in the UI directory ( in React; the components dir from in Solid). Don't import components that haven't been added, and don't re-add ones already installed.
- Find the component —
- React: to discover components (community registries too).
- Solid: no — browse zaidan.carere.dev / shadcn-solid.com in the browser.
- Get docs and examples —
- React:
npx shadcn@latest docs <component>
for URLs, then fetch; use / to preview.
- Solid: no /preview flags — read the docs site, and preview via or by reading the copy-paste source after .
- Install —
npx shadcn@latest add <component>
(React) or npx shadcn-solid@latest add <component>
(Solid).
- Review added files — read every added file; fix composition issues, icon imports, and alias paths. Solid: also run the bundle-weight check (references/primitives-solid.md) — hand-roll or trim components that pull Kobalte without an a11y payoff.
- Apply styling rules — use semantic tokens, , the variant pattern, and mobile-first responsive styles per the Critical Rules above.
- Format & lint — detect the project formatter from scripts and run it on every changed file. A non-zero exit is a blocking failure — do not declare the task done until it passes. See references/formatting.md for the detection heuristic and per-tool commands.
CVA vs cn
See references/cva.md for the full guide.
Quick rule:
- lives in component definitions — it encodes what variants a component supports.
- lives at call sites — it merges incoming with cva output and handles conditionals.
tsx
const buttonVariants = cva("inline-flex items-center ...", {
variants: { variant: { default: "bg-primary ...", outline: "border ..." } },
defaultVariants: { variant: "default" },
});
function Button({ className, variant, ...props }) {
return (
<button className={cn(buttonVariants({ variant }), className)} {...props} />
);
}
Responsive Design
See references/responsive.md for breakpoint reference and patterns.
Key rules:
- Write base (mobile) styles first, then layer breakpoint modifiers upward: , , .
- Flex by default. for vertical stacks, for horizontal.
- Grid only when genuinely two-dimensional — real rows and columns (data tables, calendar grids). Never use grid as a substitute for flex on a single axis.
tsx
// Good: flex column on mobile, row on desktop
<div className="flex flex-col gap-4 md:flex-row md:items-center">
References
Shared (both modes):
- references/theming.md — Full OKLCH token table, mapping, adding custom tokens
- references/cva.md — vs , variant patterns, compound variants
- references/responsive.md — Breakpoints, flex/grid decision guide, common responsive patterns
React mode:
- references/cli.md — : adding, previewing, searching registries, docs, project info
Solid mode:
- references/primitives-solid.md — Kobalte/Zaidan authoring idioms, primitive APIs, and the bundle-weight selective-adoption discipline
- references/cli-shadcn-solid.md — : the thin CLI surface, Zaidan delivery, browser-docs workflow
Tooling:
- references/formatting.md — Formatter detection heuristic and per-tool commands (Biome, Prettier+ESLint, ESLint-only)