Install
openclaw skills install tailwind-v4-shadcnConfigures Tailwind v4 with shadcn/ui using a mandatory four-step CSS variable theming to enable automatic dark mode and prevent common errors.
openclaw skills install tailwind-v4-shadcnProduction-tested setup for Tailwind v4 with shadcn/ui. Prevents 8 documented errors through a mandatory four-step architecture.
Complete Tailwind v4 + shadcn/ui configuration:
@theme inline, @apply, or @layer base issuestailwind v4, tailwindcss 4, shadcn, shadcn/ui, @theme inline, dark mode, css variables, vite, tw-animate-css, tailwind config, migration
Production verified: WordPress Auditor (https://wordpress-auditor.webfonts.workers.dev)
Versions: tailwindcss@4.1.18, @tailwindcss/vite@4.1.18
npx clawhub@latest install tailwind-v4-shadcn
# 1. Install dependencies
pnpm add tailwindcss @tailwindcss/vite
pnpm add -D @types/node tw-animate-css
pnpm dlx shadcn@latest init
# 2. Delete v3 config (v4 doesn't use it)
rm tailwind.config.ts
vite.config.ts:
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
import path from 'path'
export default defineConfig({
plugins: [react(), tailwindcss()],
resolve: { alias: { '@': path.resolve(__dirname, './src') } }
})
components.json (CRITICAL):
{
"tailwind": {
"config": "",
"css": "src/index.css",
"baseColor": "slate",
"cssVariables": true
}
}
Skipping steps breaks theming. Follow exactly:
/* src/index.css */
@import "tailwindcss";
@import "tw-animate-css";
:root {
--background: hsl(0 0% 100%);
--foreground: hsl(222.2 84% 4.9%);
--primary: hsl(221.2 83.2% 53.3%);
--primary-foreground: hsl(210 40% 98%);
/* ... all light mode colors with hsl() wrapper */
}
.dark {
--background: hsl(222.2 84% 4.9%);
--foreground: hsl(210 40% 98%);
--primary: hsl(217.2 91.2% 59.8%);
--primary-foreground: hsl(222.2 47.4% 11.2%);
/* ... all dark mode colors */
}
Critical: Define at root level (NOT inside @layer base). Use hsl() wrapper.
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
/* ... map ALL CSS variables */
}
Why: Generates utility classes (bg-background, text-primary). Without this, utilities don't exist.
@layer base {
body {
background-color: var(--background);
color: var(--foreground);
}
}
Critical: Reference variables directly. Never double-wrap: hsl(var(--background)).
<div className="bg-background text-foreground">
{/* Theme switches automatically via .dark class */}
</div>
No dark: variants needed for semantic colors.
hsl() in :root/.dark: --bg: hsl(0 0% 100%);@theme inline to map all CSS variables"tailwind.config": "" in components.jsontailwind.config.ts if exists@tailwindcss/vite plugin (NOT PostCSS):root/.dark inside @layer base.dark { @theme { } } (v4 doesn't support nested @theme)hsl(var(--background))tailwind.config.ts for theme@apply with @layer base/components classesdark: variants for semantic colorsError: Cannot find module 'tailwindcss-animate'
# Wrong (v3 package)
npm install tailwindcss-animate
# Correct (v4 package)
pnpm add -D tw-animate-css
@import "tailwindcss";
@import "tw-animate-css";
Error: bg-primary doesn't apply styles
Cause: Missing @theme inline mapping
@theme inline {
--color-primary: var(--primary);
/* Map ALL variables */
}
Cause: Missing ThemeProvider
See templates/theme-provider.tsx and wrap your app.
Error: Unexpected config file
rm tailwind.config.ts # v4 doesn't use this
Cause: @theme inline bakes values at build time
Use @theme (without inline) for multi-theme systems:
/* For multi-theme (not just light/dark) */
@theme {
--color-text-primary: var(--color-slate-900);
}
@layer theme {
[data-theme="dark"] {
--color-text-primary: var(--color-white);
}
}
Error: Cannot apply unknown utility class
v4 changed @apply behavior:
/* Wrong (v3 pattern) */
@layer components {
.custom-button { @apply px-4 py-2; }
}
/* Correct (v4 pattern) */
@utility custom-button {
@apply px-4 py-2;
}
Cause: CSS layer cascade issues
/* Better: Don't use @layer base for critical styles */
body {
background-color: var(--background);
}
| Symptom | Cause | Fix |
|---|---|---|
bg-primary doesn't work | Missing @theme inline | Add mapping |
| Colors black/white | Double hsl() | Use var(--color) not hsl(var(--color)) |
| Dark mode stuck | Missing ThemeProvider | Wrap app |
| Build fails | tailwind.config.ts exists | Delete file |
| Animation errors | Wrong package | Use tw-animate-css |
v4 uses OKLCH for perceptually uniform colors. Automatic sRGB fallbacks generated.
@theme {
/* Modern approach */
--color-brand: oklch(0.7 0.15 250);
/* Legacy (still works) */
--color-brand: hsl(240 80% 60%);
}
<div className="@container">
<div className="@md:text-lg @lg:grid-cols-2">
Content responds to container width
</div>
</div>
<p className="line-clamp-3">Truncate to 3 lines...</p>
@import "tailwindcss";
@plugin "@tailwindcss/typography";
@plugin "@tailwindcss/forms";
tailwind.config.ts@theme inlinetailwindcss-animate → tw-animate-cssrequire() → @plugin@apply in @layer components → @utility// Before: Hardcoded + dark variants
<div className="bg-blue-50 dark:bg-blue-950 text-blue-700 dark:text-blue-300">
// After: Semantic + automatic
<div className="bg-info/10 text-info">
ring-3 to match v3)@tailwindcss/typography or custom)templates/index.css - Complete CSS with all variablestemplates/theme-provider.tsx - Dark mode providertemplates/vite.config.ts - Vite configurationtemplates/components.json - shadcn/ui v4 configtemplates/utils.ts - cn() utilityreferences/architecture.md - Deep dive on four-step patternreferences/migration-guide.md - Semantic color migrationreferences/dark-mode.md - Complete dark mode setup@tailwindcss/vite installedvite.config.ts uses tailwindcss() plugincomponents.json has "config": ""tailwind.config.ts existssrc/index.css follows 4-step pattern:root or .dark inside @layer basetailwind.config.ts with v4 (it's ignored)hsl(var(--background))tailwindcss-animate (use tw-animate-css)@apply on @layer base/components classes in v4@theme inline step