React shadcn ui
Skill event4u-app/agent-config/dist/agent-src/skills/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
14.9 KB, ~3.8k 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 CI-enforced bylint_design_quality.
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)
Default path = bundled scripts/shadcn_add.ts + components.json; works on most projects, stays default. Modern registry model is OPT-IN — no round-trips on every op. JSON-schema + namespace detail lazy-loaded from reference/registry.md; read only on this path.
shadcn info --json handshake — run as grounding step WHEN components.json declares custom/namespaced registries, OR theme-alignment in scope. Returns framework / aliases / installed components / icon lib / base settings. NOT a forced first action on every add (over-gating, low ROI vanilla).
- Precedence: prefer live
shadcn info --json; fall back tostate.ui_audit.shadcn_inventory(existing-ui-audit) when CLI/MCP unreachable. Live read wins.
Namespaced installs — @ns/item resolves via the registries map to a registry-item.json URL (see reference). view @ns/item before add. Honour registryDependencies (install the graph, incl. version-pinned GitHub refs acme/ui/button#v1.2.0); keep propose-never-silent-run + --dry-run.
Token-aware scaffolding — registry-item.json cssVars (OKLCH light/dark/theme) → align to project tokens (info --json / components.json / state.ui_audit.design_tokens); never inject the default shadcn neutral theme (flagged anti-slop tell: default theme + Inter + neutral grays).
MCP path (opt-in) — shadcn MCP server: browse / search-across-registries / install-with-NL over MCP; wire per mcp. Alternative to CLI, never a hard dep. Decision: CLI = default + universal; MCP = opt-in when 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
DESIGN.md ## Taste Dials → honour: Variance → layout-family spread + asymmetry; Motion → animation budget + reduced-motion posture; Density → spacing scale + info-per-viewport. Absent → follow brief's inferred dials.
Component workshop (Storybook) — when the library is large enough
Generic "isolate + document reusable components" principle → fe-design § Component Architecture; this is the React carve-out for the tool-specific part.
- When it pays off — real, growing shared-component library (more than a handful of reused primitives, multiple consumers, ongoing UI work): Storybook makes each component discoverable, reviewable in isolation, reused not re-invented. Skip for a small surface of one-offs — setup + maintenance not worth it yet.
- Story per reusable primitive, not per screen — a story covers a component + its states (default / loading / empty / error / dark), mirroring the Step 3 state-coverage matrix. Screens composed, not story-fied.
- Reuse the token layer — stories render under the same semantic tokens +
.darkclass; never hardcode a preview theme (Step 2 token discipline). - A11y in-workshop — run the a11y addon so isolation catches contrast / role / focus before the component reaches a screen.
- Do NOT let stories drift from the component API — a story props-drilling values the component no longer accepts is stale documentation; keep beside the component, update 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