--- name: styling-with-tailwind description: 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.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. --- # Styling with Tailwind CSS Build accessible UIs using Tailwind CSS v4 utility classes and shadcn/ui component patterns. **Tailwind v4 uses CSS-first configuration only - never create or modify `tailwind.config.js`/`tailwind.config.ts`.** Supports Radix UI (default) or Base UI as primitive libraries. ## Critical Rules ### No `tailwind.config.js` - CSS-First Only Tailwind v4 configures everything in CSS. Migrate any JS/TS config: - `theme.extend.colors` → `@theme { --color-*: ... }` - `plugins` → `@plugin "..."` or `@utility` - `content` → `@source "..."` - `tailwindcss-animate` → `@import "tw-animate-css"` - `@layer utilities` → `@utility name { ... }` ### Always Use Semantic Color Tokens ```tsx // CORRECT - respects themes and dark mode
// WRONG - breaks theming
``` Always pair `bg-*` with `text-*-foreground`. Extend with success/warning/info in [theming.md](theming.md). ### Never Build Class Names Dynamically ```tsx // WRONG - breaks Tailwind scanner
// CORRECT - complete strings via lookup const colorMap = { red: "bg-red-500", blue: "bg-blue-500" } as const
``` ### cn() Merge Order Defaults first, consumer `className` last (tailwind-merge last-wins): ```tsx className={cn(buttonVariants({ variant, size }), className)} // correct className={cn(className, buttonVariants({ variant, size }))} // wrong ``` ### Animation Performance ```tsx // WRONG - transition-all causes layout thrashing
// CORRECT - transition only what changes
// CORRECT - respect reduced motion
``` ### `@theme` vs `@theme inline` - `@theme` - static tokens, overridable by plugins - `@theme inline` - references CSS variables, follows dark mode changes ```css @theme { --color-brand: oklch(0.6 0.2 250); } /* static */ @theme inline { --color-primary: var(--primary); } /* dynamic */ ``` See [components.md](components.md) for more pitfalls and [theming.md](theming.md) for color system reference. ## Core Patterns ### CSS Variables for Theming shadcn/ui uses semantic CSS variables mapped to Tailwind utilities: ```css /* 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:** ```tsx // Background colors omit the "-background" suffix
``` ### Component Authoring Pattern Components use plain functions with `data-slot` attributes (React 19 - no `forwardRef`): ```tsx import { 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) { return ( ``` **Icon spacing with `data-icon`:** ```tsx ``` ### Responsive Design Mobile-first breakpoints: ```tsx // Stack on mobile, grid on tablet+
// Hide on mobile
// Different layouts per breakpoint
// Responsive text sizes

``` ### Container Queries First-class in Tailwind v4, no plugin needed: ```tsx
Responds to container width, not viewport
// Named containers