React shadcn ui
Skill event4u-app/agent-config/dist/agent-src/skills/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
14.9 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 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