How shadcn/ui theming works
shadcn/ui components are built on CSS custom properties (CSS variables) defined in your globals.css. Unlike typical component libraries, shadcn/ui is not a package you install — you copy component source code into your project and style it through CSS variables.
The default variable set uses HSL format for all colors:
/* globals.css */
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--primary: 262.1 83.3% 57.8%;
--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: 262.1 83.3% 57.8%;
--radius: 0.5rem;
}
The shadcn token system
- background / foreground: page background and default text
- card / card-foreground: card component surface
- popover / popover-foreground: dropdown and tooltip surface
- primary / primary-foreground: main action color
- secondary / secondary-foreground: secondary actions
- muted / muted-foreground: placeholder text, subtle UI
- accent / accent-foreground: highlight states
- destructive / destructive-foreground: error/delete actions
- border: component borders
- input: input field borders
- ring: focus rings
- radius: global border radius
Dark mode in shadcn/ui
shadcn/ui uses a .dark class on the <html> element to switch themes:
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--primary: 263.4 70% 50.4%;
--primary-foreground: 210 40% 98%;
/* ... */
}
Customizing shadcn/ui colors step by step
1. Choose your brand primary
Convert your brand hex to HSL. The easiest way: open browser devtools, set background: #yourhex on any element, then inspect the computed HSL value. Or use a converter tool.
2. Derive foreground colors for contrast
For each color token you set, choose a foreground that achieves at minimum 4.5:1 contrast ratio (WCAG AA for normal text). Typically white works on saturated darks and black works on light/pastel colors.
3. Update muted and accent
Muted should be a very low-saturation version of your primary hue. Accent typically matches your secondary or is a slightly tinted version of the background.
4. Check ring and border
Ring should match your primary (visible focus indicator). Border should be a subtly visible neutral against your card/background.
Using LiveTheme to generate shadcn/ui themes
LiveTheme's editor supports shadcn/ui as a first-class export target. The workflow:
- Open the editor and choose or customize your colors
- Switch to a shadcn/ui preview template (Shadcn Login, Dashboard 01, or Shadcn Mock)
- See your colors applied to real shadcn components in real-time
- Click Export → shadcn/ui to get the ready-to-paste CSS for both light and dark modes
shadcn/ui with Tailwind v4
shadcn/ui is migrating to use OKLCH-based CSS variables in the Tailwind v4 format. The new variable format will look like:
:root {
--primary: oklch(0.53 0.28 291);
--primary-foreground: oklch(0.97 0.02 291);
}
LiveTheme generates OKLCH-native export code, ready for this migration.
Common shadcn/ui theming mistakes
- Forgetting dark mode variants — always define
.dark { }overrides. - Ignoring ring — a poorly chosen ring color makes keyboard navigation invisible.
- Setting muted the same as background — muted should be slightly more prominent than the background.
- Not updating Card foreground — card-foreground often defaults to the same as foreground, but on colored card surfaces it needs adjustment.