Loading...
Loading...
Build UIs with Tailwind CSS v4 and shadcn/ui. Covers CSS variables with OKLCH colors, component variants with CVA, responsive design, dark mode, and Tailwind v4.2 features. Supports Radix UI and Base UI primitives, CLI 3.0, and visual styles. Use when building interfaces with Tailwind, styling shadcn/ui components, implementing themes, or working with utility-first CSS. Triggers on tailwind, shadcn, utility classes, CSS variables, OKLCH, component styling, theming, dark mode, radix ui.
npx skill4agent add tenequm/skills shadcn-tailwindtailwind.config.jstailwind.config.tstailwind.config.jstheme.extend.colors@theme { --color-*: ... }plugins@plugin "..."@utilitycontent@source "..."tailwindcss-animate@import "tw-animate-css"@layer utilities@utility name { ... }// CORRECT - respects themes and dark mode
<div className="bg-primary text-primary-foreground">
// WRONG - breaks theming
<div className="bg-blue-500 text-white">bg-*text-*-foreground// WRONG - breaks Tailwind scanner
<div className={`bg-${color}-500`}>
// CORRECT - complete strings via lookup
const colorMap = { red: "bg-red-500", blue: "bg-blue-500" } as const
<div className={colorMap[color]}>classNameclassName={cn(buttonVariants({ variant, size }), className)} // correct
className={cn(className, buttonVariants({ variant, size }))} // wrong// WRONG - transition-all causes layout thrashing
<div className="transition-all duration-300">
// CORRECT - transition only what changes
<div className="transition-colors duration-200">
// CORRECT - respect reduced motion
<div className="motion-safe:animate-fade-in">@theme@theme inline@theme@theme inline@theme { --color-brand: oklch(0.6 0.2 250); } /* static */
@theme inline { --color-primary: var(--primary); } /* dynamic *//* globals.css - Light mode */
:root {
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--border: oklch(0.922 0 0);
--radius: 0.5rem;
}
/* Dark mode */
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0);
}
/* Tailwind v4: Map variables */
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-primary: var(--primary);
}// Background colors omit the "-background" suffix
<div className="bg-primary text-primary-foreground">
<div className="bg-muted text-muted-foreground">
<div className="bg-destructive text-destructive-foreground">data-slotforwardRefimport { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground shadow 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",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 px-3 text-xs",
lg: "h-10 px-8",
icon: "size-9",
},
},
defaultVariants: { variant: "default", size: "default" },
}
)
// Plain function with React.ComponentProps (not forwardRef)
function Button({
className,
variant,
size,
...props
}: React.ComponentProps<"button"> & VariantProps<typeof buttonVariants>) {
return (
<button
data-slot="button"
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
)
}
// Usage
<Button variant="outline" size="sm">Click me</Button>data-icon<Button>
<Spinner data-icon="inline-start" />
Generating...
</Button>
<Button>
<CheckIcon data-slot="icon" />
Save Changes
</Button>// Stack on mobile, grid on tablet+
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
// Hide on mobile
<div className="hidden md:block">
// Different layouts per breakpoint
<div className="flex flex-col md:flex-row lg:gap-8">
<aside className="w-full md:w-64">
<main className="flex-1">
</div>
// Responsive text sizes
<h1 className="text-3xl md:text-4xl lg:text-5xl"><div className="@container">
<div className="grid gap-4 @sm:grid-cols-2 @lg:grid-cols-3">
Responds to container width, not viewport
</div>
</div>
// Named containers
<div className="@container/sidebar">
<nav className="hidden @md/sidebar:block">// Use dark: prefix - but prefer semantic colors over manual dark: overrides
<div className="bg-background text-foreground"> // auto dark mode
<div className="bg-white dark:bg-black"> // manual override
// Use next-themes for toggle: useTheme() → setTheme("dark" | "light")
// Always add suppressHydrationWarning to <html> to prevent flash<div className="rounded-xl border bg-card text-card-foreground shadow">
<div className="flex flex-col space-y-1.5 p-6">
<h3 className="font-semibold leading-none tracking-tight">Title</h3>
<p className="text-sm text-muted-foreground">Description</p>
</div>
<div className="p-6 pt-0">Content</div>
<div className="flex items-center p-6 pt-0">Footer</div>
</div><div className="space-y-2">
<label className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
Email
</label>
<input
type="email"
className="flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
/>
<p className="text-sm text-muted-foreground">Helper text</p>
</div>const badgeVariants = cva(
"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors",
{
variants: {
variant: {
default: "border-transparent bg-primary text-primary-foreground shadow",
secondary: "border-transparent bg-secondary text-secondary-foreground",
destructive: "border-transparent bg-destructive text-destructive-foreground",
outline: "text-foreground",
},
},
}
)<div className="flex min-h-screen items-center justify-center">
<div className="w-full max-w-md space-y-8 p-8">
{/* Content */}
</div>
</div><div className="flex h-screen">
<aside className="w-64 border-r bg-muted/40">Sidebar</aside>
<main className="flex-1 overflow-auto">Content</main>
</div><div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<Card className="col-span-2">Wide card</Card>
<Card>Regular</Card>
<Card>Regular</Card>
<Card className="col-span-4">Full width</Card>
</div><button className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"><span className="sr-only">Close dialog</span><button className="disabled:cursor-not-allowed disabled:opacity-50" disabled>@import "tailwindcss";
/* Custom utilities (replaces @layer utilities) */
@utility tab-highlight-none {
-webkit-tap-highlight-color: transparent;
}
/* Custom variants */
@custom-variant pointer-fine (@media (pointer: fine));
/* Source control */
@source inline("{hover:,}bg-red-{50,100,200}");
@source not "./legacy";@theme {
--color-primary: oklch(0.205 0 0);
--font-sans: "Inter", system-ui;
}
/* With CSS variables (shadcn/ui pattern) */
@theme inline {
--color-primary: var(--primary);
}@import "tw-animate-css";<div className="animate-fade-in">
<div className="animate-slide-in-from-top">// Text shadows
<h1 className="text-shadow-sm text-4xl font-bold">
// Gradient masks for fade effects
<div className="mask-linear-to-b">Fades to transparent at bottom</div>
// Pointer-aware sizing
<button className="pointer-fine:py-1 pointer-coarse:py-3">
// Form validation after user interaction
<input className="user-valid:border-success user-invalid:border-destructive" />
// Overflow wrap for long strings
<p className="overflow-wrap-anywhere">verylongwordthatneedstowrap</p>// Logical block properties (RTL/writing-mode aware)
<div className="pbs-4 pbe-8 mbs-2">
<div className="border-bs-2 border-be">
// Logical sizing (replaces w-*/h-* for logical layouts)
<div className="inline-full block-screen">
// Font feature settings
<p className="font-features-['tnum','liga']">Tabular numbers</p>
// New color palettes: mauve, olive, mist, taupe
<div className="bg-mauve-100 text-olive-900">start-*end-*inset-s-*inset-e-*oklch(lightness chroma hue)// Good - uses theme
<div className="bg-background text-foreground">
// Avoid - hardcoded
<div className="bg-white text-black dark:bg-zinc-950"><div className="
flex items-center justify-between
rounded-lg border
bg-card text-card-foreground
p-4 shadow-sm
hover:bg-accent
">// Prefer design tokens
<div className="p-4 text-sm">
// Avoid when unnecessary
<div className="p-[17px] text-[13px]"># Create new project with visual style + primitive selection
npx shadcn create
# Initialize in existing project
pnpm dlx shadcn@latest init
# Add components
pnpm dlx shadcn@latest add button card form
# Add from community registries
npx shadcn add @acme/button @internal/auth-system
# View/search registries
npx shadcn view @acme/auth-system
npx shadcn search @tweakcn -q "dark"
npx shadcn list @acme
# Add MCP server for AI integration
npx shadcn@latest mcp init
# Update existing components
pnpm dlx shadcn@latest add button --overwritenpx shadcn create@theme inlinesuppressHydrationWarning<html>attribute="class"tailwind.config.jsnpx @tailwindcss/upgrade@theme@utility@plugin@source@theme@theme inlinetailwind.config.jsbg-blue-500bg-primarybg-*text-*-foregroundclassNametransition-all@theme inline@themedata-slotmotion-safe:motion-reduce: