React shadcn ui
Use when building React UI on shadcn/ui primitives + Tailwind — the apply/review/polish skill dispatched by `directives/ui/*` for the `react-shadcn` stack.From its SKILL.md
npx -y skills add event4u-app/agent-config --skill react-shadcn-uiAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 7 stars7 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
15.5 KB, ~3.9k tokens by cl100k_base, as published. Nobody here has run it
react-shadcn-ui
Grounded stack guidance: pull idiomatic Do/Don't + docs URLs via
./scripts-run <skills-root>/corpus-grounding/scripts/ground search --manifest <skills-root>/design-intelligence/data/manifest.json --stack shadcn "<topic>"(also--stack react,--stack nextjs). Seedesign-intelligence.
Component installer — scripts/shadcn_add.ts (gated, assisted)
Bundled installer (Apache-2.0-derived, see header + design-intelligence/ATTRIBUTION.md)
wraps npx shadcn@latest add <components> — the only subprocess+network
surface in the adopted suite. Per runtime-safety + the execution
block above:
- Propose, never silent-run — always show the exact
npxcommand + component list first (use--dry-run); the user confirms before any live run. - Missing tool → per
missing-tool-handling: ifnpx/Node is absent, STOP and ask (install vs. manual component copy) — never silently work around. - Verify after run — confirm the component landed
(
components/ui/<name>.tsxexists,components.jsonunchanged or sanely updated) before reporting success.
Compatibility
- Tested against:
[email protected], Tailwind CSS3.x, React18+. - The audit step (
directives/ui/audit.ts) reads the line above and compares it withstate.ui_audit.shadcn_inventory.version; a major mismatch triggers a soft halt before this skill runs.
When to use
Use when state.stack.frontend == "react-shadcn" and directives/ui/apply.ts,
review.ts, or polish.ts dispatches to this skill, or when a React project
clearly uses shadcn/ui (presence of components.json, @radix-ui/*
dependencies, a components/ui/ folder of generated primitives).
Do NOT use when:
- Project is Blade + Livewire + Flux (use
flux/livewire/blade-ui). - Project is Vue (use the Vue stack skills).
- Plain React without shadcn/ui — fall back to manual composition; this skill assumes the primitive set exists.
Gotcha
- shadcn/ui is not an npm package. Primitives are copied into
components/ui/and edited in-place. Do notnpm install shadcn-ui. Runnpx shadcn@latest add <primitive>to scaffold; then edit. - Major-version drift between this skill's
## Compatibilityline and the project's installed primitives is a real risk. The audit step writesstate.ui_audit.shadcn_inventorywith the detected version — when it diverges by a major, audit emits a soft halt before this skill runs. - shadcn/ui composes Radix primitives. Accessibility is built in via Radix
but only when you use the wrapper components correctly (
asChild,<DialogTrigger>instead of a bare<button>). - Tailwind tokens come from
tailwind.config.{js,ts}(theme.extend.colors) and CSS custom properties on:rootand.dark(--background,--foreground,--primary,--ring, …). Audit writes them intostate.ui_audit.design_tokens. Use those tokens; do not hardcode values. - Dark mode is class-based (
<html class="dark">). Every color must come frombg-background,text-foreground, etc. — never rawbg-white. - Every interactive primitive must declare a focus-visible state via
focus-visible:ring-2 focus-visible:ring-ring; that comes for free with the generated primitives but is easy to remove during a refactor. - Anti-AI-slop: shadcn-default look. The out-of-the-box shadcn
theme +
Inter-as-system-fallback + neutral grays reads as template across projects (catalog T7/T8 + C5). Unlessstate.ui_audit.design_tokenspins the neutral palette as the project's identity, the polish step should match typography and color tokens to the design brief'saesthetic:line (fromfe-designaesthetic-direction). Theme/font drift within a single audited project breaks consistency — variation lives between projects, not between components in the same surface. - Anti-AI-slop catalog + linter. Pull
docs/guidelines/design-antipatterns.mdbefore the polish step (Visual V1–V7, Layout L1–L8 are the React-component slop tells); the objective quality floors (WCAG contrast, focus-visible, reduced-motion) are validated viaaccessibility-auditor's checklist — cite its verdict rather than eyeballing.
Covered primitives
This skill is validated against the following shadcn primitives at the declared version:
- Form / inputs:
Button,Input,Textarea,Checkbox,RadioGroup,Select,Switch,Label,Form(react-hook-form wrapper +zodResolver). - Overlay:
Dialog,Sheet,Popover,Tooltip,DropdownMenu,AlertDialog. - Layout:
Card,Separator,Tabs,Accordion,ScrollArea. - Data display:
Table(with@tanstack/react-table),Badge,Avatar,Skeleton,Progress. - Feedback:
Toast(sonner),Alert.
Not covered — fall back to manual composition
- Marketing-only components (Hero, Pricing, Features) — outside shadcn/ui.
Calendar/DatePicker— composition skill required, not generated.Combobox— built fromCommand+Popover; case-by-case.- Streaming / partial-prerender boundaries — use the project's framework patterns (Next.js / Remix), not shadcn/ui.
Registry & MCP awareness (opt-in)
The default path is the bundled scripts/shadcn_add.ts CLI wrapper + reading
components.json — it works on most shadcn projects and stays the default.
The modern registry model is an opt-in enhancement; do not add round-trips
to every component op. Full JSON-schema + namespace detail is lazy-loaded from
reference/registry.md — read it only on this path,
not on the vanilla add.
shadcn info --json handshake — run it as the grounding step when the
project declares custom/namespaced registries in components.json, OR when
theme-alignment is in scope. It returns framework, aliases, installed
components, icon lib, and base settings. Do NOT make it a forced first action
on every add (over-gating; low ROI on vanilla projects).
- Precedence vs our own audit: prefer a live
shadcn info --jsonwhen available; fall back tostate.ui_audit.shadcn_inventory(fromexisting-ui-audit) when the CLI/MCP is not reachable. They answer the same question (project context) — the live read wins.
Namespaced installs — @ns/item resolves via the registries map to a
registry-item.json URL (see the reference). Run view @ns/item to inspect
the JSON before add. Honour registryDependencies (install the graph,
including version-pinned GitHub refs like acme/ui/button#v1.2.0); keep
propose-never-silent-run + --dry-run.
Token-aware scaffolding — when a registry-item.json carries cssVars
(OKLCH, light/dark/theme), align additions to the project's existing tokens
(from info --json / components.json / state.ui_audit.design_tokens) —
never inject the default shadcn neutral theme (it is a flagged anti-slop
tell: default theme + Inter fallback + neutral grays).
MCP path (opt-in) — the shadcn MCP server exposes browse / search-across-
registries / install-with-natural-language over MCP; configure per the
mcp skill. It is an alternative to the CLI, never a hard
dependency. Decision note: CLI path = default + universal; MCP path = opt-in
when the user has it configured; registry-JSON literacy underpins both.
Procedure: render a shadcn/ui component for the design brief
Step 0: Inspect
- Read
state.ui_audit.shadcn_inventory.versionand confirm it matches the version in## Compatibilitywithin the same major. If audit flagged a mismatch, the user already chose to proceed — note that instate.changes. - Read
state.ui_audit.design_tokens— every color, spacing, and radius in the rendered output must reference a token from this map. - Read
state.ui_design:components→ the primitive list to compose.microcopy→ button labels, empty-state text, validation messages. Lock — render verbatim.states→ empty / loading / error / success / disabled coverage.a11y→ ARIA labels, keyboard nav, focus order.
Step 1: Compose primitives
- Import primitives from the project's
components/ui/path (@/components/ui/button, …) — never fromshadcnorradix-ui. - Compose Radix-style:
<Dialog>→<DialogTrigger asChild>→<DialogContent>→<DialogHeader>→<DialogTitle>. Never wrapDialogTriggeraround a pre-styled<button>; passasChild. - Use the variant API of
Button(variant="default" | "destructive" | "outline" | "secondary" | "ghost" | "link"); do not override with raw Tailwind for the variant set. - Forms:
useForm(react-hook-form) +zodResolver(schema)→<Form>→<FormField>→<FormItem>→<FormLabel>→<FormControl>→<FormMessage>. Validation messages come from the zod schema, mirrored to the design-brief microcopy.
Step 2: Apply tokens, dark mode, a11y
- Colors via semantic classes:
bg-background,text-foreground,bg-primary text-primary-foreground,text-muted-foreground. Nobg-white/text-black/ hardcoded#fff. - Spacing / radius from theme tokens (
rounded-lgmapped to--radiusintailwind.config.{js,ts}). Polish refactors hardcoded values when a token equivalent exists. - Dark mode: never branch on a
darkprop; rely on the.darkclass on the root and semantic tokens. - Every interactive primitive: keyboard trigger present (Enter/Space
on buttons, Esc on dialogs — Radix free), visible focus ring,
aria-labelfromstate.ui_design.a11ywhen icon-only.
Step 3: State coverage
- Empty: render the design-brief empty-state copy in a
Cardor inline placeholder; nevernull. - Loading:
Skeletonrows for tables;Buttondisabled+Loader2icon for submit-in-flight. - Error:
Alert variant="destructive"with the design-brief message;FormMessagefor field-level errors. - Success:
toast.success(...)fromsonnerwith the design-brief confirmation copy. - Disabled:
disabledprop on the trigger plus the design-brief reason asaria-describedbytext.
Step 4: Validate
- No raw
<input>/<button>/<select>outside the primitive set. - No hardcoded colors / spacing — every value is a token.
- Microcopy matches
state.ui_design.microcopybyte-for-byte. - Dark mode: toggle
.darkon<html>, render the component, every surface still legible (notext-white on bg-white). - Keyboard: Tab through every focusable element; focus ring visible.
Output format
- React component file(s) under the project's
components/(orapp/) tree, importing primitives from@/components/ui/*. - Per file, one entry recorded in
state.changeswithkind="ui",stack="react-shadcn", and the design-brief summary.
Review pass — a11y findings + preview envelope
When this skill is dispatched by directives/ui/review.ts (test slot)
or directives/ui/polish.ts (verify slot) — i.e. a review/polish run,
not the initial apply — it also emits:
state.ui_review.a11y—{violations: [{rule, selector, severity}, ...], severity_floor?, accepted_violations?}. Run an a11y tool against the rendered output (e.g.axe-corevia Playwright,@axe-core/react,jest-axe) and translate hits into this shape. Use the same(rule, selector)shape asstate.ui_audit.a11y_baselineso the engine's de-dup matches pre-existing entries on replay. Omit the envelope on apply passes; the engine's_apply_a11y_gateonly fires when a baseline is present.state.ui_review.preview—{render_ok: bool, screenshot_path?, dom_dump_path?, error?, skipped?, skip_reason?}. Render evidence is required, not optional on a review/polish pass: you MUST drive the headless browser (Playwright + axe-core) against the rendered output and writerender_ok. Omitting it now triggers thepreview_render_requiredhalt — a render-capable stack can no longer claim success without rendering.render_ok: falsewitherrorpopulated triggers thepreview_render_failedhalt;render_ok: truewithscreenshot_paththreads the screenshot into the delivery report'sartifactslist. The only no-render path is an explicit, reasoned skip: setskipped: trueplus askip_reason(e.g. no Playwright runner in this env). Browser tooling (Playwright/Cypress/…) is a consumer-project dependency — this package does not ship one.
Polish dispatch: when the dispatcher skips review because a previous
review pass already returned SUCCESS, this skill MUST itself
synthesise the updated state.ui_review.findings (including any
remaining a11y_violation entries) so the engine's gate sees the
current state on the next polish round.
Taste Dials
When DESIGN.md declares ## Taste Dials, honour them: Variance → layout-family spread + asymmetry tolerance; Motion → animation budget + reduced-motion posture; Density → spacing scale + information-per-viewport. Absent → follow the design brief's inferred dials.
Component workshop (Storybook) — when the library is large enough
The generic "isolate + document reusable components" principle lives in
fe-design § Component Architecture; this is the React
carve-out for the tool-specific part.
- When it pays off — a real, growing shared-component library (roughly: more than a handful of reused primitives, multiple consumers, ongoing UI work). Storybook makes each component discoverable, reviewable in isolation, and reused instead of re-invented. Skip it for a small surface of one-off components — the setup + maintenance is not worth it yet.
- Story per reusable primitive, not per screen — a story covers a component and its states (default / loading / empty / error / dark), mirroring the Step 3 state-coverage matrix. Screens are composed, not story-fied.
- Reuse the token layer — stories render under the same semantic tokens +
.darkclass; never hardcode a preview theme (same token discipline as Step 2). - A11y in-workshop — run the a11y addon so the isolation catches contrast / role / focus issues before the component reaches a screen.
- Do NOT let stories drift from the component API — a story that props-drills values the component no longer accepts is stale documentation; keep them beside the component and update them in the same change.
Do NOT
- Do NOT install
shadcn-uifrom npm — primitives are scaffolded. - Do NOT hardcode colors / spacing / radii — use the token map.
- Do NOT branch on a
darkprop — use semantic tokens + the.darkclass. - Do NOT rewrite microcopy — it is locked by
state.ui_design. - Do NOT skip
asChildonDialogTrigger/SheetTrigger/ similar Radix wrappers — it breaks the accessibility contract. - Do NOT introduce a non-shadcn UI library (MUI, Chakra) into the same surface — pick one system per surface.
Auto-trigger keywords
- shadcn / shadcn ui / shadcn/ui
- React component (when the project uses shadcn)
- Radix primitive
- Tailwind dark mode
- React Hook Form + zod
What ships with it: 2 files
16.6 KB alongside SKILL.md, 1 of them executable
reference/
- registry.md2.8 KB
scripts/
- shadcn_add.tsruns13.8 KB