| name | shadcn-patterns |
| description | Build and customize UI components with shadcn/ui — component installation, CSS variable theming, Radix UI primitive composition, variant patterns with cva(), dark mode wiring, and extending components without forking. Use when asked about "shadcn", "shadcn/ui", "shadcn component", "radix primitive", "cva variants", "shadcn theme", "extend shadcn", "shadcn dark mode", "shadcn form", "shadcn dialog", "shadcn button variants", or "how to customize shadcn components". Do NOT use for: full design system token architecture — see design-system-gen. Do NOT use for: animation patterns — see motion-design.
|
| origin | adapted:MIT © shadcn-ui |
| license | MIT © 2026 Vũ Văn Tâm |
| version | 1.0.0 |
| compatibility | shadcn/ui (any version), Radix UI, Next.js / Vite / Remix. Requires Tailwind CSS. |
When to Use
- Use when: adding shadcn/ui to a project for the first time
- Use when: extending a shadcn component with new variants
- Use when: theming shadcn to match a brand (CSS variables)
- Use when: building a form with shadcn Form + react-hook-form + zod
- Do NOT use for: building a full token system from scratch — design-system-gen
- Do NOT use for: non-Tailwind component libraries
Setup
npx shadcn@latest init
npx shadcn@latest add button
npx shadcn@latest add dialog form input label select toast
npx shadcn@latest add button --overwrite
Components land in components/ui/ — they're yours, modify freely.
CSS Variable Theming
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222 84% 5%;
--primary: 221 83% 53%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96%;
--secondary-foreground: 222 47% 11%;
--muted: 210 40% 96%;
--muted-foreground: 215 16% 47%;
--accent: 210 40% 96%;
--destructive: 0 84% 60%;
--border: 214 32% 91%;
--input: 214 32% 91%;
--ring: 221 83% 53%;
: ;
}
{
: ;
: ;
: ;
: ;
: ;
}
}
Extending Components with cva()
import { cva, type VariantProps } from 'class-variance-authority';
const buttonVariants = cva(
'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 disabled:opacity-50 disabled:pointer-events-none',
{
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 hover:bg-accent hover:text-accent-foreground',
ghost: 'hover:bg-accent hover:text-accent-foreground',
'ghost-destructive': 'hover:bg-destructive/10 hover:text-destructive',
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 px-3 rounded-md',
lg: 'h-11 px-8 rounded-md',
icon: 'h-10 w-10',
},
},
defaultVariants: { variant: 'default', size: 'default' },
}
);
interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
<typeof buttonVariants> {
?: ;
}
< variant= size=></>
shadcn Form (react-hook-form + zod)
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
const schema = z.object({
email: z.string().email('Invalid email'),
password: z.string().min(8, 'At least 8 characters'),
});
type FormValues = z.infer<typeof schema>;
function LoginForm() {
const form = useForm<FormValues>({ resolver: zodResolver(schema) });
return (
<Form {}>
(
Email
{/* auto-shows zod error */}
)} />
Login
);
}
Composing Radix Primitives
import {
Dialog, DialogContent, DialogDescription, DialogFooter,
DialogHeader, DialogTitle, DialogTrigger,
} from '@/components/ui/dialog';
<Dialog>
<DialogTrigger asChild>
<Button>Open</Button> {/* asChild renders Trigger as Button, not extra DOM */}
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Confirm delete</DialogTitle>
<DialogDescription>This action is permanent.</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button variant="destructive">Delete</Button>
</DialogFooter>
</DialogContent>
</Dialog>
Anti-Fake-Pass Rules
Before claiming shadcn/ui implementation is done, you MUST show:
Reference: gates/anti-fake-pass-gate.md