git clone https://github.com/wpank/ai
T=$(mktemp -d) && git clone --depth=1 https://github.com/wpank/ai "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/frontend/tailwind-v4-shadcn" ~/.claude/skills/wpank-ai-tailwind-v4-shadcn-ui-stack && rm -rf "$T"
skills/frontend/tailwind-v4-shadcn/SKILL.mdTailwind v4 + shadcn/ui Stack
Production-tested setup for Tailwind v4 with shadcn/ui. Prevents 8 documented errors through a mandatory four-step architecture.
WHAT
Complete Tailwind v4 + shadcn/ui configuration:
- Four-step theming architecture (mandatory)
- CSS variable-based color system
- Automatic dark mode switching
- Error prevention for 8 common mistakes
- Migration guide from v3
- Production-ready templates
WHEN
- Starting a new React/Vite project with Tailwind v4
- Migrating from Tailwind v3 to v4
- Setting up shadcn/ui with Tailwind v4
- Debugging: colors not working, dark mode broken, build failures
- Fixing
,@theme inline
, or@apply
issues@layer base
KEYWORDS
tailwind 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
Installation
OpenClaw / Moltbot / Clawbot
npx clawhub@latest install tailwind-v4-shadcn
Quick Start
# 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 } }
The Four-Step Architecture (MANDATORY)
Skipping steps breaks theming. Follow exactly:
Step 1: Define CSS Variables at Root
/* 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.
Step 2: Map Variables to Tailwind
@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.
Step 3: Apply Base Styles
@layer base { body { background-color: var(--background); color: var(--foreground); } }
Critical: Reference variables directly. Never double-wrap:
hsl(var(--background)).
Step 4: Result - Automatic Dark Mode
<div className="bg-background text-foreground"> {/* Theme switches automatically via .dark class */} </div>
No
dark: variants needed for semantic colors.
Critical Rules
Always Do
- Wrap colors with
inhsl()
/:root
:.dark--bg: hsl(0 0% 100%); - Use
to map all CSS variables@theme inline - Set
in components.json"tailwind.config": "" - Delete
if existstailwind.config.ts - Use
plugin (NOT PostCSS)@tailwindcss/vite
Never Do
- Put
/:root
inside.dark@layer base - Use
(v4 doesn't support nested @theme).dark { @theme { } } - Double-wrap:
hsl(var(--background)) - Use
for themetailwind.config.ts - Use
with@apply
classes@layer base/components - Use
variants for semantic colorsdark:
Common Errors & Solutions
Error 1: tw-animate-css Import
Error:
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 2: Colors Not Working
Error:
bg-primary doesn't apply styles
Cause: Missing
@theme inline mapping
@theme inline { --color-primary: var(--primary); /* Map ALL variables */ }
Error 3: Dark Mode Not Switching
Cause: Missing ThemeProvider
See
templates/theme-provider.tsx and wrap your app.
Error 4: Build Fails
Error:
Unexpected config file
rm tailwind.config.ts # v4 doesn't use this
Error 5: @theme inline Breaks Multi-Theme
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 6: @apply Breaking
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; }
Error 7: @layer base Styles Ignored
Cause: CSS layer cascade issues
/* Better: Don't use @layer base for critical styles */ body { background-color: var(--background); }
Quick Reference
| Symptom | Cause | Fix |
|---|---|---|
doesn't work | Missing | Add mapping |
| Colors black/white | Double | Use not |
| Dark mode stuck | Missing ThemeProvider | Wrap app |
| Build fails | exists | Delete file |
| Animation errors | Wrong package | Use |
Tailwind v4 New Features
OKLCH Color Space
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%); }
Container Queries (Built-in)
<div className="@container"> <div className="@md:text-lg @lg:grid-cols-2"> Content responds to container width </div> </div>
Line Clamp (Built-in)
<p className="line-clamp-3">Truncate to 3 lines...</p>
Plugins
@import "tailwindcss"; @plugin "@tailwindcss/typography"; @plugin "@tailwindcss/forms";
Migration from v3
Key Changes
- Delete
tailwind.config.ts - Move theme to CSS with
@theme inline - Replace
→tailwindcss-animatetw-animate-css - Replace
→require()@plugin
in@apply
→@layer components@utility
Color Migration
// 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">
Visual Changes
- Ring width default: 3px → 1px (use
to match v3)ring-3 - Heading styles removed from Preflight (add via
or custom)@tailwindcss/typography
Files
- Complete CSS with all variablestemplates/index.css
- Dark mode providertemplates/theme-provider.tsx
- Vite configurationtemplates/vite.config.ts
- shadcn/ui v4 configtemplates/components.json
-templates/utils.ts
utilitycn()
- Deep dive on four-step patternreferences/architecture.md
- Semantic color migrationreferences/migration-guide.md
- Complete dark mode setupreferences/dark-mode.md
Setup Checklist
-
installed@tailwindcss/vite -
usesvite.config.ts
plugintailwindcss() -
hascomponents.json"config": "" - NO
existstailwind.config.ts -
follows 4-step patternsrc/index.css - ThemeProvider wraps app
- Theme toggle works
NEVER
- Put
or:root
inside.dark@layer base - Use
with v4 (it's ignored)tailwind.config.ts - Double-wrap colors:
hsl(var(--background)) - Use
(usetailwindcss-animate
)tw-animate-css - Use
on@apply
classes in v4@layer base/components - Skip the
step@theme inline