12 KiB
12 KiB
name, description
| name | description |
|---|---|
| styling-with-tailwind | Creates UIs using Tailwind CSS utility classes and shadcn/ui patterns. Covers CSS variables with OKLCH colors, component variants with CVA, responsive design, dark mode, and Tailwind v4 features. Use when building interfaces with Tailwind, styling shadcn/ui components, implementing themes, or working with utility-first CSS. |
Styling with Tailwind CSS
Build accessible UIs using Tailwind utility classes and shadcn/ui component patterns.
Core Patterns
CSS Variables for Theming
shadcn/ui uses semantic CSS variables mapped to Tailwind utilities:
/* 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);
}
Usage in components:
// 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">
Component Variants with CVA
Use class-variance-authority for component variants:
import { cva } from "class-variance-authority"
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",
},
}
)
// Usage
<Button variant="outline" size="sm">Click me</Button>
Responsive Design
Mobile-first breakpoints:
// 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">
Dark Mode
// Use dark: prefix for dark mode styles
<div className="bg-white dark:bg-black text-black dark:text-white">
// Theme toggle component
"use client"
import { Moon, Sun } from "lucide-react"
import { useTheme } from "next-themes"
export function ThemeToggle() {
const { theme, setTheme } = useTheme()
return (
<button onClick={() => setTheme(theme === "dark" ? "light" : "dark")}>
<Sun className="rotate-0 scale-100 dark:-rotate-90 dark:scale-0" />
<Moon className="absolute rotate-90 scale-0 dark:rotate-0 dark:scale-100" />
</button>
)
}
Common Component Patterns
Card
<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>
Form Field
<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>
Badge
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",
},
},
}
)
Alert
<div className="relative w-full rounded-lg border px-4 py-3 text-sm [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg+div]:translate-y-[-3px] [&:has(svg)]:pl-11">
<AlertCircle className="size-4" />
<div className="font-medium">Alert Title</div>
<div className="text-sm text-muted-foreground">Description</div>
</div>
Loading Skeleton
<div className="space-y-2">
<div className="h-4 w-[250px] animate-pulse rounded bg-muted" />
<div className="h-4 w-[200px] animate-pulse rounded bg-muted" />
</div>
Layout Patterns
Centered Layout
<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>
Sidebar Layout
<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>
Dashboard Grid
<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>
Container with Max Width
<div className="container mx-auto px-4 md:px-6 lg:px-8">
<div className="max-w-2xl mx-auto">
{/* Centered content */}
</div>
</div>
Accessibility Patterns
Focus Visible
<button className="focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2">
Screen Reader Only
<span className="sr-only">Close dialog</span>
Disabled States
<button className="disabled:cursor-not-allowed disabled:opacity-50" disabled>
ARIA-friendly Alert
<div role="alert" className="rounded-lg border p-4">
<div className="flex items-start gap-3">
<AlertCircle className="size-5 text-destructive" />
<div className="flex-1 space-y-1">
<h5 className="font-medium">Error</h5>
<p className="text-sm text-muted-foreground">Message</p>
</div>
</div>
</div>
Tailwind v4 Features
Size Utility
// New syntax (replaces w-* h-*)
<div className="size-4">
<div className="size-8">
<div className="size-full">
@theme Directive
/* Tailwind v4 syntax */
@theme {
--color-primary: oklch(0.205 0 0);
--font-sans: "Inter", system-ui;
}
/* With CSS variables */
@theme inline {
--color-primary: var(--primary);
}
Animation
/* globals.css */
@import "tw-animate-css";
<div className="animate-fade-in">
<div className="animate-slide-in-from-top">
<div className="animate-spin">
Tailwind v4.1 Features (April 2025)
Text Shadow:
// Subtle text shadows for depth
<h1 className="text-shadow-sm text-4xl font-bold">
<h2 className="text-shadow-md text-2xl">
<div className="text-shadow-lg text-xl">
// Custom text shadows
<div className="text-shadow-[0_2px_4px_rgb(0_0_0_/_0.1)]">
Mask Utilities:
// Gradient masks for fade effects
<div className="mask-linear-to-b from-black to-transparent">
Fades to transparent at bottom
</div>
// Image masks
<div className="mask-[url('/mask.svg')]">
Masked content
</div>
// Common patterns
<div className="mask-radial-gradient">Spotlight effect</div>
Colored Drop Shadow:
// Brand-colored shadows
<div className="drop-shadow-[0_4px_12px_oklch(0.488_0.243_264.376)]">
// Use with semantic colors
<Button className="drop-shadow-lg drop-shadow-primary/50">
Glowing button
</Button>
Overflow Wrap:
// Break long words
<p className="overflow-wrap-anywhere">
verylongwordthatneedstowrap
</p>
<p className="overflow-wrap-break-word">
URLs and long strings
</p>
OKLCH Colors
Use OKLCH for better color perception:
/* Format: oklch(lightness chroma hue) */
--primary: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
/* Benefits: perceptually uniform, consistent lightness across hues */
Base Color Palettes
shadcn/ui provides multiple base colors:
/* Neutral (default) - pure grayscale */
--primary: oklch(0.205 0 0);
/* Zinc - cooler, blue-gray */
--primary: oklch(0.21 0.006 285.885);
/* Slate - balanced blue-gray */
--primary: oklch(0.208 0.042 265.755);
/* Stone - warmer, brown-gray */
--primary: oklch(0.216 0.006 56.043);
Best Practices
Prefer Semantic Colors
// Good - uses theme
<div className="bg-background text-foreground">
// Avoid - hardcoded
<div className="bg-white text-black dark:bg-zinc-950">
Group Related Utilities
<div className="
flex items-center justify-between
rounded-lg border
bg-card text-card-foreground
p-4 shadow-sm
hover:bg-accent
">
Avoid Arbitrary Values
// Prefer design tokens
<div className="p-4 text-sm">
// Avoid when unnecessary
<div className="p-[17px] text-[13px]">
Installation
# Initialize shadcn/ui
pnpm dlx shadcn@latest init
# Add components
pnpm dlx shadcn@latest add button card form
# Add all components
pnpm dlx shadcn@latest add --all
Troubleshooting
Colors not updating:
- Check CSS variable in globals.css
- Verify @theme inline includes variable
- Clear build cache
Dark mode not working:
- Verify ThemeProvider wraps app
- Check suppressHydrationWarning on html tag
- Ensure dark: variants defined
Tailwind v4 migration:
- Run
@tailwindcss/upgrade@nextcodemod - Update CSS variables with hsl() wrappers
- Change @theme to @theme inline
- Install tw-animate-css
Component Patterns
For detailed component patterns see components.md:
- Composition: asChild pattern for wrapping elements
- Typography: Heading scales, prose styles, inline code
- Forms: React Hook Form with Zod validation
- Icons: Lucide icons integration and sizing
- Inputs: OTP, file, grouped inputs
- Dialogs: Modal patterns and composition
- Data Tables: TanStack table integration
- Toasts: Sonner notifications
- CLI: Complete command reference
Resources
See theming.md for complete color system reference and examples.
Summary
Key concepts:
- Use semantic CSS variables for theming
- Apply CVA for component variants
- Follow mobile-first responsive patterns
- Implement dark mode with next-themes
- Use OKLCH for modern color handling
- Prefer Tailwind v4 features (size-*, @theme)
- Always ensure accessibility with focus-visible, sr-only
This skill focuses on shadcn/ui patterns with Tailwind CSS. For component-specific examples, refer to the official shadcn/ui documentation.