# shadcn/ui Theming & Customization Theme configuration, CSS variables, dark mode, and component customization. ## Dark Mode Setup ### Next.js App Router **1. Install next-themes:** ```bash npm install next-themes ``` **2. Create theme provider:** ```tsx // components/theme-provider.tsx "use client" import * as React from "react" import { ThemeProvider as NextThemesProvider } from "next-themes" export function ThemeProvider({ children, ...props }: React.ComponentProps) { return {children} } ``` **3. Wrap app:** ```tsx // app/layout.tsx import { ThemeProvider } from "@/components/theme-provider" export default function RootLayout({ children }) { return ( {children} ) } ``` **4. Theme toggle component:** ```tsx import { Moon, Sun } from "lucide-react" import { useTheme } from "next-themes" import { Button } from "@/components/ui/button" export function ThemeToggle() { const { setTheme, theme } = useTheme() return ( ) } ``` ### Vite / Other Frameworks Use similar approach with next-themes or implement custom solution: ```javascript // Store preference function toggleDarkMode() { const isDark = document.documentElement.classList.toggle('dark') localStorage.setItem('theme', isDark ? 'dark' : 'light') } // Initialize on load if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) { document.documentElement.classList.add('dark') } ``` ## CSS Variable System shadcn/ui uses CSS variables for theming. Variables defined in `globals.css`: ```css @layer base { :root { --background: 0 0% 100%; --foreground: 222.2 84% 4.9%; --primary: 222.2 47.4% 11.2%; --primary-foreground: 210 40% 98%; --secondary: 210 40% 96.1%; --secondary-foreground: 222.2 47.4% 11.2%; --muted: 210 40% 96.1%; --muted-foreground: 215.4 16.3% 46.9%; --accent: 210 40% 96.1%; --accent-foreground: 222.2 47.4% 11.2%; --destructive: 0 84.2% 60.2%; --destructive-foreground: 210 40% 98%; --border: 214.3 31.8% 91.4%; --input: 214.3 31.8% 91.4%; --ring: 222.2 84% 4.9%; --radius: 0.5rem; } .dark { --background: 222.2 84% 4.9%; --foreground: 210 40% 98%; --primary: 210 40% 98%; --primary-foreground: 222.2 47.4% 11.2%; --secondary: 217.2 32.6% 17.5%; --secondary-foreground: 210 40% 98%; --muted: 217.2 32.6% 17.5%; --muted-foreground: 215 20.2% 65.1%; --accent: 217.2 32.6% 17.5%; --accent-foreground: 210 40% 98%; --destructive: 0 62.8% 30.6%; --destructive-foreground: 210 40% 98%; --border: 217.2 32.6% 17.5%; --input: 217.2 32.6% 17.5%; --ring: 212.7 26.8% 83.9%; } } ``` ### Color Format Values use HSL format without `hsl()` wrapper for better opacity control: ```css --primary: 222.2 47.4% 11.2%; /* H S L */ ``` Usage in Tailwind: ```css background: hsl(var(--primary)); background: hsl(var(--primary) / 0.5); /* 50% opacity */ ``` ## Tailwind Configuration Map CSS variables to Tailwind utilities: ```ts // tailwind.config.ts export default { darkMode: ["class"], theme: { extend: { colors: { border: "hsl(var(--border))", input: "hsl(var(--input))", ring: "hsl(var(--ring))", background: "hsl(var(--background))", foreground: "hsl(var(--foreground))", primary: { DEFAULT: "hsl(var(--primary))", foreground: "hsl(var(--primary-foreground))", }, secondary: { DEFAULT: "hsl(var(--secondary))", foreground: "hsl(var(--secondary-foreground))", }, destructive: { DEFAULT: "hsl(var(--destructive))", foreground: "hsl(var(--destructive-foreground))", }, muted: { DEFAULT: "hsl(var(--muted))", foreground: "hsl(var(--muted-foreground))", }, accent: { DEFAULT: "hsl(var(--accent))", foreground: "hsl(var(--accent-foreground))", }, }, borderRadius: { lg: "var(--radius)", md: "calc(var(--radius) - 2px)", sm: "calc(var(--radius) - 4px)", }, }, }, } ``` ## Color Customization ### Method 1: Update CSS Variables Change colors by modifying CSS variables in `globals.css`: ```css :root { --primary: 262.1 83.3% 57.8%; /* Purple */ --primary-foreground: 210 20% 98%; } .dark { --primary: 263.4 70% 50.4%; /* Darker purple */ --primary-foreground: 210 20% 98%; } ``` ### Method 2: Theme Generator Use shadcn/ui theme generator: https://ui.shadcn.com/themes Select base color, generate theme, copy CSS variables. ### Method 3: Multiple Themes Create theme variants with data attributes: ```css [data-theme="violet"] { --primary: 262.1 83.3% 57.8%; --primary-foreground: 210 20% 98%; } [data-theme="rose"] { --primary: 346.8 77.2% 49.8%; --primary-foreground: 355.7 100% 97.3%; } ``` Apply theme: ```tsx
``` ## Component Customization Components live in your codebase - modify directly. ### Customize Variants ```tsx // components/ui/button.tsx const buttonVariants = cva( "inline-flex items-center justify-center rounded-md text-sm font-medium", { variants: { variant: { default: "bg-primary text-primary-foreground", destructive: "bg-destructive text-destructive-foreground", outline: "border border-input bg-background", // Add custom variant gradient: "bg-gradient-to-r from-purple-500 to-pink-500 text-white", }, size: { default: "h-10 px-4 py-2", sm: "h-9 rounded-md px-3", lg: "h-11 rounded-md px-8", // Add custom size xl: "h-14 rounded-md px-10 text-lg", }, }, defaultVariants: { variant: "default", size: "default", }, } ) ``` Usage: ```tsx ``` ### Customize Styles Modify base styles in component: ```tsx // components/ui/card.tsx const Card = React.forwardRef< HTMLDivElement, React.HTMLAttributes >(({ className, ...props }, ref) => (
)) ``` ### Override with className Pass additional classes to override: ```tsx Custom styled card ``` ## Base Color Presets shadcn/ui provides base color presets during `init`: - **Slate**: Cool gray tones - **Gray**: Neutral gray - **Zinc**: Warm gray - **Neutral**: Balanced gray - **Stone**: Earthy gray Select during setup or change later by updating CSS variables. ## Style Variants Two component styles available: - **Default**: Softer, more rounded - **New York**: Sharp, more contrast Select during `init` or in `components.json`: ```json { "style": "new-york", "tailwind": { "cssVariables": true } } ``` ## Radius Customization Control border radius globally: ```css :root { --radius: 0.5rem; /* Default */ --radius: 0rem; /* Sharp corners */ --radius: 1rem; /* Rounded */ } ``` Components use radius variable: ```tsx className="rounded-lg" /* Uses var(--radius) */ ``` ## Best Practices 1. **Use CSS Variables**: Enables runtime theme switching 2. **Consistent Foreground Colors**: Pair each color with appropriate foreground 3. **Test Both Themes**: Verify components in light and dark modes 4. **Semantic Naming**: Use `destructive` not `red`, `muted` not `gray` 5. **Accessibility**: Maintain sufficient color contrast (WCAG AA minimum) 6. **Component Overrides**: Use `className` prop for one-off customization 7. **Extract Patterns**: Create custom variants for repeated customizations