How Tailwind and shadcn/ui work together
Tailwind CSS and shadcn/ui are not alternatives — they are layers that work together.
Tailwind CSS is the styling engine. It generates utility classes like bg-primary, text-muted-foreground, rounded-lg, and px-4. It does not provide components — it provides the vocabulary for styling them.
shadcn/ui components are React components that use Tailwind utility classes internally. When a Button renders with variant destructive, it applies Tailwind classes like bg-destructive text-destructive-foreground. The component maps variant names to Tailwind classes using class-variance-authority (CVA).
The theme bridges them. The @theme block in globals.css defines CSS custom properties (--color-primary, --color-destructive, --radius-lg). Tailwind reads these and generates matching utility classes (bg-primary, rounded-lg). shadcn/ui components reference the same utility classes.
globals.css @theme -> Tailwind reads tokens -> generates utility classes
|
shadcn/ui Button -> uses bg-primary class -> resolves to --color-primary
Changing --color-primary in the theme automatically changes every shadcn/ui component that uses the primary color. No component code needs to change.
The theme block
The theme is defined in app/globals.css using Tailwind CSS 4 syntax:
@import "tailwindcss";
@theme {
--color-primary: hsl(213 76% 44%); /* brand blue #1b66c3 */
--color-primary-foreground: hsl(0 0% 100%);
--color-secondary: hsl(210 40% 96%);
--color-destructive: hsl(0 84% 60%);
--color-warning: hsl(30 94% 54%); /* brand orange #f88b1b */
--color-success: hsl(142 71% 45%);
--color-background: hsl(0 0% 100%);
--color-foreground: hsl(222 47% 11%);
--color-muted: hsl(210 40% 96%);
--color-border: hsl(214 32% 91%);
--color-ring: hsl(213 76% 44%);
--radius-sm: 0.25rem;
--radius-md: 0.375rem;
--radius-lg: 0.5rem;
--radius-xl: 0.75rem;
}
The theme also includes a dark mode block inside @layer base that overrides all color tokens when the .dark class is applied to the document root.
Color system
Every color has a base and a -foreground pair. The base is the background/fill color; the foreground is the text color that sits on top of it. This ensures contrast.
- primary (blue #1b66c3) — main brand actions, links, focus rings
- secondary — subtle backgrounds, secondary buttons
- destructive (red) — delete actions, error states
- warning (orange #f88b1b) — caution badges, CTA accents, pending states
- success (green) — confirmation, active/verified badges
- muted — disabled states, placeholder text
- accent — hover backgrounds, subtle emphasis
- background/foreground — page body defaults
- border — all border colors
- ring — focus ring color (matches primary)
- input — form input borders
Radius tokens
Border radius tokens control the roundness of all components:
--radius-sm(0.25rem) — small elements like badges--radius-md(0.375rem) — inputs, buttons--radius-lg(0.5rem) — cards, dialogs--radius-xl(0.75rem) — large containers
Dark mode
Dark mode is supported via the @custom-variant dark declaration:
@custom-variant dark (&:where(.dark, .dark *));
Adding the dark class to the HTML root switches all Tailwind dark-mode variants. The theme toggle component handles this. Dark-mode color overrides can be added inside a @media (prefers-color-scheme: dark) block or by defining dark-specific values in the @theme block.
Base layer
The @layer base block sets global defaults:
- All elements get
border-border(consistent border color) - Body gets
bg-background text-foreground antialiased - Smooth scrolling enabled
- Links get
transition-colorsfor hover effects
Customizing the theme
To change the site appearance:
- Edit the HSL values in the
@themeblock inglobals.css - Every shadcn/ui component and every Tailwind utility class that references those tokens updates automatically
- No component code changes needed
For a completely different color scheme (e.g. a mortgage directory vs. real estate), only globals.css needs to change. The components and registry remain the same.