# Customization & Theming Components reference semantic CSS variable tokens. Change the variables to change every component. ## Contents - How it works (CSS variables → Tailwind utilities → components) - Color variables and OKLCH format - Dark mode setup - Changing the theme (presets, CSS variables) - Adding custom colors (Tailwind v3 and v4) - Border radius - Customizing components (variants, className, wrappers) - Checking for updates --- ## How It Works 1. CSS variables defined in `:root` (light) and `.dark` (dark mode). 2. Tailwind maps them to utilities: `bg-primary`, `text-muted-foreground`, etc. 3. Components use these utilities — changing a variable changes all components that reference it. --- ## Color Variables Every color follows the `name` / `name-foreground` convention. The base variable is for backgrounds, `-foreground` is for text/icons on that background. | Variable | Purpose | | -------------------------------------------- | -------------------------------- | | `--background` / `--foreground` | Page background and default text | | `--card` / `--card-foreground` | Card surfaces | | `--primary` / `--primary-foreground` | Primary buttons and actions | | `--secondary` / `--secondary-foreground` | Secondary actions | | `--muted` / `--muted-foreground` | Muted/disabled states | | `--accent` / `--accent-foreground` | Hover and accent states | | `--destructive` / `--destructive-foreground` | Error and destructive actions | | `--border` | Default border color | | `--input` | Form input borders | | `--ring` | Focus ring color | | `--chart-1` through `--chart-5` | Chart/data visualization | | `--sidebar-*` | Sidebar-specific colors | | `--surface` / `--surface-foreground` | Secondary surface | Colors use OKLCH: `--primary: oklch(0.205 0 0)` where values are lightness (0–1), chroma (0 = gray), and hue (0–360). --- ## Dark Mode Class-based toggle via `.dark` on the root element. In Next.js, use `next-themes`: ```tsx import { ThemeProvider } from "next-themes" {children} ``` --- ## Changing the Theme ```bash # Apply a preset code from ui.shadcn.com. npx shadcn@latest init --preset a2r6bw --force # Switch to a named preset. npx shadcn@latest init --preset radix-nova --force npx shadcn@latest init --reinstall # update existing components to match # Use a custom theme URL. npx shadcn@latest init --preset "https://ui.shadcn.com/init?base=radix&style=nova&theme=blue&..." --force ``` Or edit CSS variables directly in `globals.css`. --- ## Adding Custom Colors Add variables to the file at `tailwindCssFile` from `npx shadcn@latest info` (typically `globals.css`). Never create a new CSS file for this. ```css /* 1. Define in the global CSS file. */ :root { --warning: oklch(0.84 0.16 84); --warning-foreground: oklch(0.28 0.07 46); } .dark { --warning: oklch(0.41 0.11 46); --warning-foreground: oklch(0.99 0.02 95); } ``` ```css /* 2a. Register with Tailwind v4 (@theme inline). */ @theme inline { --color-warning: var(--warning); --color-warning-foreground: var(--warning-foreground); } ``` When `tailwindVersion` is `"v3"` (check via `npx shadcn@latest info`), register in `tailwind.config.js` instead: ```js // 2b. Register with Tailwind v3 (tailwind.config.js). module.exports = { theme: { extend: { colors: { warning: "oklch(var(--warning) / )", "warning-foreground": "oklch(var(--warning-foreground) / )", }, }, }, } ``` ```tsx // 3. Use in components.
Warning
``` --- ## Border Radius `--radius` controls border radius globally. Components derive values from it (`rounded-lg` = `var(--radius)`, `rounded-md` = `calc(var(--radius) - 2px)`). --- ## Customizing Components See also: [rules/styling.md](./rules/styling.md) for Incorrect/Correct examples. Prefer these approaches in order: ### 1. Built-in variants ```tsx ``` ### 2. Tailwind classes via `className` ```tsx ... ``` ### 3. Add a new variant Edit the component source to add a variant via `cva`: ```tsx // components/ui/button.tsx warning: "bg-warning text-warning-foreground hover:bg-warning/90", ``` ### 4. Wrapper components Compose shadcn/ui primitives into higher-level components: ```tsx export function ConfirmDialog({ title, description, onConfirm, children }) { return ( {children} {title} {description} Cancel Confirm ) } ``` --- ## Checking for Updates ```bash npx shadcn@latest add button --diff ``` To preview exactly what would change before updating, use `--dry-run` and `--diff`: ```bash npx shadcn@latest add button --dry-run # see all affected files npx shadcn@latest add button --diff button.tsx # see the diff for a specific file ``` See [Updating Components in SKILL.md](./SKILL.md#updating-components) for the full smart merge workflow.