Theming
Purpose and Scope
Theming in shadcn/ui is centered on semantic CSS variables rather than one-off utility classes. A semantic token is a named value such as background, foreground, or primary that describes a role in the interface instead of a literal color. Components use those roles by default, so changing the CSS variable behind a role updates the look of the application without rewriting every component. This page explains the repository’s documented theming model, how components.json enables it, how dark mode reuses the same token names, and how newer preset workflows build on the same foundation.
Sources: apps/v4/content/docs/(root)/theming.mdx
The important design constraint is that shadcn/ui distributes code you own. Theme defaults are meant to be good enough to start with, but not a permanent visual identity that every app must share. The changelog for npx shadcn create states that the project goal has always been customization: solid defaults, spacing, color tokens, animations, and accessibility, followed by the expectation that teams tweak the code, add components, and build their own version. The theming API is therefore both a CSS contract and part of the project’s open-code customization story.
Sources: apps/v4/content/docs/changelog/2025-12-shadcn-create.mdx
Relevant Source Files
apps/v4/content/docs/(root)/theming.mdx- Primary theming documentation for CSS variables, semantic tokens,components.jsonconfiguration, dark-mode token overrides, token naming conventions, and the theme-token reference.apps/v4/content/docs/components/aria/chart.mdx- Component-level example showing chart-specific CSS variables, OKLCH color values, light and dark definitions, and how component families can extend the global token model.apps/v4/content/docs/changelog/2024-08-npx-shadcn-init.mdx- CLI context for initializing projects, updating Tailwind files, installing themes and components, and maintainingcomponents.jsonaliases after the CLI rewrite.apps/v4/content/docs/changelog/2025-04-shadcn-2-5.mdx- Registry installation context for resolving files and imports anywhere in an app, which matters when themed components or registry items are installed outside a fixed folder layout.apps/v4/content/docs/changelog/2025-12-shadcn-create.mdx- Product direction for visually creating presets that include component library choice, icons, base color, theme, fonts, spacing, and component-code transformations.apps/v4/content/docs/changelog/2026-04-shadcn-apply.mdx- Preset-application workflow for existing projects, including updates to theme, colors, CSS variables, fonts, and icons while preserving current base and RTL settings.
Core Theme Model
The documented default is to use CSS variables for theming. In components.json, that is represented by tailwind.cssVariables: true, with the theming guide describing this as the default. The same configuration block also shows tailwind.css, tailwind.baseColor, and the selected style, which together tell the CLI and generated components where the global CSS lives and which base palette to start from. Tailwind then maps variables into utilities such as bg-background, text-foreground, border-border, and ring-ring, letting component code stay readable while remaining themeable.
Sources: apps/v4/content/docs/(root)/theming.mdx
{
"style": "base-nova",
"rsc": true,
"tailwind": {
"config": "",
"css": "app/globals.css",
"baseColor": "neutral",
"cssVariables": true
}
}A small utility example captures the whole model: an element using bg-background text-foreground does not hard-code a color pair. Instead, background and foreground resolve through CSS variables defined in the project CSS. This is why theming is broad but low-friction: components can keep their default class names, while application teams override the variables under :root and .dark. If a project wants a different visual direction, it should usually change tokens first, then only customize component source when the structure, spacing, or behavior also needs to diverge.
Sources: apps/v4/content/docs/(root)/theming.mdx
<div className="bg-background text-foreground" />Token Convention and Theme Tokens
The token convention uses background and foreground pairs. The base token controls the surface color, while the -foreground token controls text and icon color placed on that surface. The documentation explicitly notes that the background suffix is omitted for the surface token: primary pairs with primary-foreground, not primary-background. This convention is valuable because it keeps component APIs short while making contrast relationships explicit. When a button, card, popover, or accent state is themed, the surface and readable foreground should be considered together.
Sources: apps/v4/content/docs/(root)/theming.mdx
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);The visible token reference groups roles by where they appear in an app. background and foreground control the default app background and text. card and card-foreground support elevated surfaces such as cards, dashboard panels, and settings panels. popover and popover-foreground apply to floating surfaces, including popovers, dropdown menus, context menus, and other overlays. The action-oriented roles include primary, secondary, accent, and their foreground partners. Subtle content is represented by muted and muted-foreground, while destructive marks destructive actions, invalid states, and error emphasis.
Sources: apps/v4/content/docs/(root)/theming.mdx
| Token | Role | Typical use |
|---|---|---|
background / foreground | App shell and default text | Page backgrounds, sections, and body text |
card / card-foreground | Elevated surfaces | Cards, dashboard panels, settings panels |
popover / popover-foreground | Floating surfaces | Popover, DropdownMenu, ContextMenu, overlays |
primary / primary-foreground | High-emphasis actions | Default Button, selected states, badges, active accents |
secondary / secondary-foreground | Lower-emphasis filled actions | Secondary buttons, secondary badges, supporting UI |
muted / muted-foreground | Subtle surfaces and content | Descriptions, placeholders, empty states, helper text |
accent / accent-foreground | Interactive state surfaces | Ghost buttons, menu highlights, hovered rows, selected items |
destructive | Error and destructive emphasis | Destructive buttons, invalid states, destructive menu items |
border | Default borders and separators | Component outlines, dividers, separators |
Dark Mode and Component-Specific Variables
Dark mode does not require a separate component implementation. The theming guide describes dark mode as overriding the same tokens inside a .dark selector. That means the class toggles which variable values are active, while component class names such as bg-background and text-foreground remain the same. The dark-mode documentation is the next step for adding a provider and toggling the .dark class, but the theming contract is already visible here: light and dark palettes are parallel sets of values for the same semantic names.
Sources: apps/v4/content/docs/(root)/theming.mdx
The chart component shows how component-specific tokens extend this model without replacing it. Manual chart installation includes adding --chart-1 through --chart-5 to the global CSS file, with separate OKLCH values under :root and .dark. The chart docs also explain that shadcn/ui does not wrap Recharts; users build charts with Recharts primitives and add shadcn/ui helpers such as ChartContainer and ChartTooltipContent only where needed. The result is a themeable component layer that still leaves the underlying chart library and data shape under application control.
Sources: apps/v4/content/docs/components/aria/chart.mdx
@layer base {
:root {
--chart-1: oklch(0.646 0.222 41.116);
--chart-2: oklch(0.6 0.118 184.704);
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
}
.dark {
--chart-1: oklch(0.488 0.243 264.376);
--chart-2: oklch(0.696 0.17 162.48);
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
}
}Presets, CLI Workflows, and Customization Boundaries
The newer customization workflows formalize what the CSS-variable model already makes possible. npx shadcn create is presented as a way to preview and generate a project preset that can include colors, radius, fonts, icons, base color, theme, component library choice, and visual style. The changelog emphasizes that this goes beyond theming because the generated configuration can rewrite component code to match preferences such as spacing, structure, and selected libraries. Use token overrides when the component structure is right; use presets or component edits when the generated source itself should change.
Sources: apps/v4/content/docs/(root)/theming.mdx, apps/v4/content/docs/changelog/2025-12-shadcn-create.mdx
For existing projects, shadcn apply is the documented path for switching presets without starting over. The changelog says the command applies a new preset, reinstalls existing components, and updates the theme, colors, CSS variables, fonts, and icons. It also preserves the current base and RTL settings even if the preset URL was generated with different values. That preservation matters because base component family and right-to-left direction are structural project choices, while a preset may primarily represent a visual direction.
Sources: apps/v4/content/docs/changelog/2026-04-shadcn-apply.mdx
npx shadcn@latest apply --preset b2D0vQ7G4The CLI rewrite context explains why these workflows can operate across real applications rather than only fresh demos. The August 2024 changelog says the CLI can install components, themes, hooks, utilities, and more with npx shadcn add, detect frameworks during initialization, update existing Tailwind files instead of overwriting them, and support monorepos. The later resolve-anywhere changelog adds that registries can place files anywhere in an app and that installation tracks files with multi-pass resolution for imports and aliases. Theming therefore depends on both CSS tokens and correct file placement.
Sources: apps/v4/content/docs/changelog/2024-08-npx-shadcn-init.mdx, apps/v4/content/docs/changelog/2025-04-shadcn-2-5.mdx
Practical Workflow
A practical theming workflow starts by accepting the generated defaults, confirming that tailwind.cssVariables is enabled, and locating the CSS file named by tailwind.css. Next, adjust semantic tokens under :root for the light palette and under .dark for the dark palette. Review common surfaces first: page background, foreground text, cards, popovers, primary actions, muted helper text, accent states, borders, and destructive states. Then test real components rather than isolated color swatches, because the token convention assumes each surface and foreground pair remains readable together.
Sources: apps/v4/content/docs/(root)/theming.mdx
When component documentation asks for additional variables, keep them near the same base layer so they participate in the same light and dark strategy. Charts are the clearest example in the supplied sources: adding --chart-1 through --chart-5 creates reusable color slots for chart series without forcing the chart component to know application-specific color names. If you need a one-off color, component docs also allow normal class-level customization in places such as custom alert colors, but the maintainable path for shared visual identity is still named tokens.
Sources: apps/v4/content/docs/components/aria/chart.mdx
Next Steps
After setting theme tokens, read the dark mode guide for provider setup and .dark class toggling, then review components.json for how Tailwind, aliases, React Server Components, and project style are configured. If the goal is a new visual identity rather than a simple color adjustment, use the create workflow to generate a preset and the apply workflow to migrate an existing project. For component-level work, inspect the relevant component docs to see whether it defines extra variables, supports class-level customization, or expects a registry-installed source file you can edit directly.
Sources: apps/v4/content/docs/(root)/theming.mdx, apps/v4/content/docs/changelog/2025-12-shadcn-create.mdx, apps/v4/content/docs/changelog/2026-04-shadcn-apply.mdx