Components never hard-code colours. They read CSS variables through Tailwind
utilities such as bg-primary, text-muted-foreground and border, so the
same component follows any theme.
| Variable | Used for |
|---|---|
--background, --foreground | Page background and body text |
--card, --card-foreground | Raised surfaces |
--popover, --popover-foreground | Menus, popovers, tooltips |
--primary, --primary-foreground | Main actions and brand accents |
--secondary, --secondary-foreground | Secondary actions |
--muted, --muted-foreground | Quiet fills and supporting text |
--accent, --accent-foreground | Hover and selected states |
--destructive | Errors and destructive actions |
--border, --input, --ring | Borders, field outlines and focus rings |
--chart-1 … --chart-5 | Chart series |
--sidebar-* | The sidebar primitive |
--radius | Base corner radius |
--font-sans | Body typeface |
Values are written in oklch(), which keeps lightness steps even when you
change the hue.
The quickest route is to pick one on /themes or a full preset on /presets and install it:
pnpm dlx shadcn@latest add https://ui.tealmac.com/r/themes/<id>.json
To edit by hand, change the values in your global stylesheet. :root holds
light mode, .dark holds dark mode:
:root {
--radius: 0.75rem;
--primary: oklch(0.52 0.13 195);
--primary-foreground: oklch(0.98 0 0);
}
.dark {
--primary: oklch(0.74 0.12 195);
--primary-foreground: oklch(0.16 0 0);
}Variables cascade, so you can theme a single region by setting them on a wrapper:
<section style={{ "--primary": "oklch(0.6 0.2 25)" } as React.CSSProperties}>
<Button>Uses the local primary</Button>
</section>Declare it in :root and .dark, then expose it to Tailwind in the @theme inline block:
:root {
--success: oklch(0.62 0.15 150);
}
.dark {
--success: oklch(0.72 0.15 150);
}
@theme inline {
--color-success: var(--success);
}Now bg-success and text-success work everywhere.
Check text against its background in both modes. The customizer on /themes flags any pairing below 4.5:1 before you install.