Capítulo 4 de 108

Theming

Core Idea

shadcn/ui theming is CSS variables mapped to Tailwind utilities via semantic tokens (background, primary, etc.), overridden inside .dark for dark mode. Change the look of the whole app by editing tokens, never by rewriting component classes.

Key Concepts

  • Background/foreground pairing: a base surface token pairs with a -foreground token for text/icon color on that surface (e.g. primary + primary-foreground); the "background" suffix is omitted on the surface token name.
  • @theme inline: Tailwind v4 block that exposes --background etc. as --color-background utilities (bg-background, text-foreground, border-border, ring-ring).
  • --radius: single base radius token; radius-sm...radius-4xl are all calc()'d from it, so changing one variable rescales every corner.
  • tailwind.cssVariables: true (default) uses CSS variables; false generates inline Tailwind color utilities (e.g. bg-zinc-950) instead. Installation-time choice; switching requires delete + reinstall.
  • tailwind.baseColor: seeds initial token values; options are Neutral, Stone, Zinc, Mauve, Olive, Mist, Taupe.

Code Examples

: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);
}

@theme inline {
  --color-warning: var(--warning);
  --color-warning-foreground: var(--warning-foreground);
}
<div className="bg-warning text-warning-foreground" />
  • O que demonstra: como adicionar um token novo (fora do default) e usá-lo como classe Tailwind.
npx shadcn@latest init --no-css-variables
  • O que demonstra: alternativa sem CSS variables, gerando utilities inline (bg-zinc-950 dark:bg-white).

Reference Tables

TokenControlsUsed by
background / foregroundDefault app bg/textPage shell, default text
card / card-foregroundElevated surfacesCard, panels
popover / popover-foregroundFloating surfacesPopover, DropdownMenu, ContextMenu
primary / primary-foregroundHigh-emphasis actionsDefault Button, selected states, badges
secondary / secondary-foregroundLower-emphasis filled actionsSecondary buttons/badges
muted / muted-foregroundSubtle surfacesDescriptions, placeholders, helper text
accent / accent-foregroundHover/focus/active surfacesGhost buttons, menu highlights
destructiveDestructive/error emphasisDestructive buttons, invalid states
border / input / ringBorders / input outline / focus ringCards, Input, focusable controls
chart-1...chart-5Chart paletteCharts
sidebar* (sidebar, sidebar-primary, sidebar-accent, sidebar-border, sidebar-ring)Sidebar-specific surfacesSidebar component
radiusBase corner radiusCards, inputs, buttons, popovers

Anti-patterns

  • Editing component classes to change color: override the CSS variable instead; keeps every component in sync.
  • Switching cssVariables mid-project without reinstalling: it's an installation-time decision, existing components won't retroactively convert.
  • Forgetting to define a new token in both :root and .dark: dark mode will silently fall back/break for that token.

Key Takeaways

  1. Always define new tokens in :root, .dark, and @theme inline (three places) to get a working Tailwind utility with dark mode support.
  2. --radius is the single lever for the entire corner-radius scale, don't hardcode radii per component.
  3. shadcn/create exists as a visual tool to preview colors/radius/fonts/icons and generate a preset.
  4. The full default neutral theme scaffold (:root/.dark/@layer base) is copy-pasteable as a starting point.

Connects To

  • components-json: tailwind.cssVariables and tailwind.baseColor fields drive this.
  • typeset: covers font tokens, a sibling concern to color tokens.