React shadcn ui
Universal AI Agent OS — audited skills, governance rules, replayable state. One contract, every host agent.
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.
What its author says it does
Copied from the file, not written here
Use when building React UI on shadcn/ui primitives + Tailwind — the apply/review/polish skill dispatched by `directives/ui/*` for the `react-shadcn` stack.
SKILL.md
15.5 KB, 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