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.json configuration, 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 maintaining components.json aliases 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

components.json
{
  "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

TokenRoleTypical use
background / foregroundApp shell and default textPage backgrounds, sections, and body text
card / card-foregroundElevated surfacesCards, dashboard panels, settings panels
popover / popover-foregroundFloating surfacesPopover, DropdownMenu, ContextMenu, overlays
primary / primary-foregroundHigh-emphasis actionsDefault Button, selected states, badges, active accents
secondary / secondary-foregroundLower-emphasis filled actionsSecondary buttons, secondary badges, supporting UI
muted / muted-foregroundSubtle surfaces and contentDescriptions, placeholders, empty states, helper text
accent / accent-foregroundInteractive state surfacesGhost buttons, menu highlights, hovered rows, selected items
destructiveError and destructive emphasisDestructive buttons, invalid states, destructive menu items
borderDefault borders and separatorsComponent 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

app/globals.css
@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 b2D0vQ7G4

The 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