dist/plugins/web-ui-shadcn-ui/skills/web-ui-shadcn-ui/SKILL.md
shadcn/ui component library patterns, CLI usage, theming, customization
npx skillsauth add agents-inc/skills web-ui-shadcn-uiInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Quick Guide: shadcn/ui is a copy-and-own component system built on Radix primitives and Tailwind. Use
npx shadcn@latest addto install components intocomponents/ui/. Theme via CSS custom properties in OKLCH format. Compose with compound component patterns. Use thecn()utility for class merging. NewFieldcomponent replaces the oldForm/FormFieldpattern for form-library-agnostic field layout.
<critical_requirements>
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST use the CLI to add components - npx shadcn@latest add [component] - not manual copy)
(You MUST customize components through CSS variables and the cn() utility - not direct style overrides)
(You MUST keep components in the components/ui/ directory - this is the shadcn convention)
(You MUST define foreground colors for every new background color - --brand needs --brand-foreground)
</critical_requirements>
Auto-detection: shadcn/ui, shadcn, @shadcn, components.json, npx shadcn, cn() utility, ui components, Radix-based components, data-slot, Field component, cva variants
When to use:
When NOT to use:
Key patterns covered:
shadcn/ui is not a traditional component library - it is how you build your component library. Components are copied into your codebase via CLI, giving you full ownership. Core functionality (accessibility, keyboard nav) comes from Radix primitives; the styling layer is yours to customize.
Five Core Principles:
What shadcn/ui handles vs what other skills handle:
Always use the CLI to add components. It resolves dependencies, installs Radix packages, and creates proper file structure.
# Initialize (creates components.json)
npx shadcn@latest init
# Add components
npx shadcn@latest add button card dialog
# Inspection flags (CLI v4)
npx shadcn@latest add button --dry-run # Preview changes
npx shadcn@latest add button --diff # Check for updates
npx shadcn@latest add button --view # Display component payload
# Monorepo support
npx shadcn@latest add button --path=packages/ui/src/components
# Project info (useful for AI agents)
npx shadcn@latest info
# View component docs from CLI (v4)
npx shadcn@latest docs combobox
Components go in components/ui/. The cn() utility goes in lib/utils.ts. Both are created automatically.
Why good: CLI handles dependency resolution, provides inspection before changes, components become owned source code
shadcn/ui uses CSS custom properties with OKLCH color format (Tailwind v4). Every color follows a background/foreground naming convention.
/* The naming convention - understand this, don't memorize values */
:root {
--primary: oklch(0.205 0 0); /* Background color */
--primary-foreground: oklch(0.985 0 0); /* Text ON that background */
}
.dark {
--primary: oklch(0.985 0 0); /* Inverted for dark mode */
--primary-foreground: oklch(0.205 0 0);
}
Key Tailwind v4 changes from older shadcn versions:
@theme inline directive maps CSS variables to Tailwind utilities@custom-variant dark defines dark mode selector--chart-* and --sidebar-* variable families for specialized components--radius-sm/md/lg/xl derived from base --radiusAdding custom colors requires both the CSS variable AND the @theme inline mapping:
:root {
--brand: oklch(0.627 0.265 303.9);
--brand-foreground: oklch(1 0 0);
}
@theme inline {
--color-brand: var(--brand);
--color-brand-foreground: var(--brand-foreground);
}
Then use as: bg-brand text-brand-foreground hover:bg-brand/90
See examples/theming.md for complete variable reference and custom color examples.
Combines clsx (conditional classes) with tailwind-merge (conflict resolution). Consumer className always comes last so overrides work.
// lib/utils.ts - auto-generated by shadcn init
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
// Usage in components - className last for consumer overrides
function Card({ className, ...props }: CardProps) {
return (
<div
className={cn("rounded-lg border bg-card shadow-sm", className)}
{...props}
/>
);
}
Critical behavior: cn("px-4", "px-8") produces "px-8" (last wins), not "px-4 px-8". This is why consumer className overrides work correctly.
See examples/core.md for more cn() examples.
Use cva (class-variance-authority) for variant-based component styling. This is how shadcn/ui itself structures Button, Badge, and Alert.
import { cva, type VariantProps } from "class-variance-authority";
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline: "border border-input bg-background hover:bg-accent",
ghost: "hover:bg-accent hover:text-accent-foreground",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 px-3",
lg: "h-11 px-8",
icon: "h-10 w-10",
},
},
defaultVariants: { variant: "default", size: "default" },
},
);
To add a new variant, edit the component source directly - you own it. To use variants: <Button variant="destructive" size="sm">.
See examples/composition.md for extended button with loading state and responsive dialog/drawer patterns.
The Field component is the modern form-library-agnostic way to compose form fields. It provides labels, descriptions, error messages, and accessibility - without coupling to any specific form library.
import {
Field,
FieldLabel,
FieldDescription,
FieldError,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
// Field is a layout primitive - bring your own form library
<Field data-invalid={hasError}>
<FieldLabel htmlFor="email">Email</FieldLabel>
<Input id="email" aria-invalid={hasError} {...fieldProps} />
<FieldDescription>We will never share your email.</FieldDescription>
{hasError && <FieldError errors={errors} />}
</Field>;
Replaces the old Form/FormField/FormItem/FormControl/FormMessage pattern which was tightly coupled to React Hook Form. The Field component works with any form library or server actions.
Related components: FieldGroup for grouping related fields, FieldSet and FieldLegend for semantic grouping.
See examples/forms.md for complete form integration examples.
shadcn/ui uses compound components (Card, Dialog, Sheet, Tabs, Command) with consistent sub-component patterns. Extend by wrapping, not replacing.
// CORRECT: Extend by wrapping compound components
function ProductCard({ title, price, ...props }: ProductCardProps) {
return (
<Card {...props}>
<CardHeader>
<CardTitle>{title}</CardTitle>
<CardDescription>${price}</CardDescription>
</CardHeader>
</Card>
);
}
// WRONG: Breaking compound structure with plain divs
<div className="card">
<div className="card-header">
<h2>{title}</h2>
</div>
</div>;
asChild prop - Use when composing interactive elements to avoid nesting (e.g., Button wrapping a Link):
<Button asChild>
<Link href="/dashboard">Dashboard</Link>
</Button>
See examples/dialogs.md for AlertDialog, Sheet, and toast patterns. See examples/data-table.md for table with row actions. See examples/command-palette.md for command menu.
Recent additions that solve common patterns:
| Component | Purpose | Install |
| ---------------- | ---------------------------------------- | ------------------------------------ |
| Field | Form-library-agnostic field layout | npx shadcn@latest add field |
| Spinner | Loading indicator (replaces custom ones) | npx shadcn@latest add spinner |
| Kbd | Keyboard shortcut display | npx shadcn@latest add kbd |
| Button Group | Grouped button container | npx shadcn@latest add button-group |
| Input Group | Input with addons (icons, buttons) | npx shadcn@latest add input-group |
| Item | Flex container for lists/cards | npx shadcn@latest add item |
| Empty | Empty state display | npx shadcn@latest add empty |
These components work across Radix and Base UI primitives.
Unified Radix UI package (Feb 2026): Individual @radix-ui/react-* packages are now a single radix-ui package. Migrate with npx shadcn@latest migrate radix.
// Old (pre-Feb 2026)
import * as DialogPrimitive from "@radix-ui/react-dialog";
// New (unified package)
import { Dialog as DialogPrimitive } from "radix-ui";
CLI v4 (March 2026):
npx shadcn@latest docs [component] - View component docs from CLI (useful for AI agents)npx shadcn@latest init --template - Full project scaffolding for Next.js, Vite, Astro, React Router, TanStack Start, Laravel--preset flag packs entire design system config (colors, fonts, radius, icons) into a shareable codeshadcn/skills - AI agent context for component patterns and registry workflowsregistry:base - Distribute entire design systems as single payloadsRTL support (Jan 2026): First-class right-to-left layout support. The CLI transforms physical CSS classes to logical equivalents at install time.
</patterns>Detailed Resources:
<red_flags>
High Priority Issues:
hsl() when they are already OKLCHMedium Priority Issues:
Gotchas & Edge Cases:
--primary-foreground is text color ON --primary background, not primary-colored textforwardRef needed; ref is now a regular prop. Components use data-slot attributesasChild required for Link composition - Prevents nested interactive elements (button inside anchor)onValueChange and defaultValue (or value)var(--chart-1) directly, no hsl() wrapper neededsuppressHydrationWarning - Required on <html> when using theme provider to prevent hydration mismatchForm/FormField/FormItem/FormControl/FormMessage are legacy; prefer Field component for new code--base radix or --base base)radix-ui (not individual @radix-ui/react-* packages). Migrate with npx shadcn@latest migrate radix</red_flags>
<critical_reminders>
All code must follow project conventions in CLAUDE.md
(You MUST use the CLI to add components - npx shadcn@latest add [component] - not manual copy)
(You MUST customize components through CSS variables and the cn() utility - not direct style overrides)
(You MUST keep components in the components/ui/ directory - this is the shadcn convention)
(You MUST define foreground colors for every new background color - --brand needs --brand-foreground)
Failure to follow these rules will break component updates, cause styling conflicts, and violate shadcn/ui conventions.
</critical_reminders>
development
Material Design component library for Vue 3
development
VitePress 1.x — Vue-powered static site generator for documentation sites, built on Vite
tools
Docusaurus 3.x documentation framework — site configuration, docs/blog plugins, sidebars, versioning, MDX, swizzling, and deployment
development
TanStack Form patterns - useForm, form.Field, validators, arrays, linked fields, createFormHook, type safety