tailwind-v4-shadcn
About
This skill provides a production-ready setup for integrating Tailwind CSS v4 with shadcn/ui in React/Vite projects. Use it when initializing new projects, implementing dark mode, debugging CSS variables, or migrating from Tailwind v3. It covers key patterns like the @theme inline configuration, CSS variable architecture, ThemeProvider setup, and common v4 migration gotchas.
Quick Install
Claude Code
Recommended/plugin add https://github.com/jezweb/claude-skillsgit clone https://github.com/jezweb/claude-skills.git ~/.claude/skills/tailwind-v4-shadcnCopy and paste this command in Claude Code to install this skill
Documentation
Tailwind v4 + shadcn/ui Production Stack
Production-tested: WordPress Auditor (https://wordpress-auditor.webfonts.workers.dev) Last Updated: 2025-11-09 Status: Production Ready ✅
⚠️ BEFORE YOU START (READ THIS!)
CRITICAL FOR AI AGENTS: If you're Claude Code helping a user set up Tailwind v4:
- Explicitly state you're using this skill at the start of the conversation
- Reference patterns from the skill rather than general knowledge
- Prevent known issues listed in
reference/common-gotchas.md - Don't guess - if unsure, check the skill documentation
USER ACTION REQUIRED: Tell Claude to check this skill first!
Say: "I'm setting up Tailwind v4 + shadcn/ui - check the tailwind-v4-shadcn skill first"
Why This Matters (Real-World Results)
Without skill activation:
- ❌ Setup time: ~5 minutes
- ❌ Errors encountered: 2-3 (tw-animate-css, duplicate @layer base)
- ❌ Manual fixes needed: 2+ commits
- ❌ Token usage: ~65k
- ❌ User confidence: Required debugging
With skill activation:
- ✅ Setup time: ~1 minute
- ✅ Errors encountered: 0
- ✅ Manual fixes needed: 0
- ✅ Token usage: ~20k (70% reduction)
- ✅ User confidence: Instant success
Known Issues This Skill Prevents
- tw-animate-css import error (deprecated in v4)
- Duplicate @layer base blocks (shadcn init adds its own)
- Wrong template selection (vanilla TS vs React)
- Missing post-init cleanup (incompatible CSS rules)
- Wrong plugin syntax (using @import or require() instead of @plugin directive)
All of these are handled automatically when the skill is active.
Quick Start (5 Minutes - Follow This Exact Order)
1. Install Dependencies
pnpm add tailwindcss @tailwindcss/vite
pnpm add -D @types/node
pnpm dlx shadcn@latest init
2. Configure Vite
// 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')
}
}
})
3. Update components.json
{
"tailwind": {
"config": "", // ← CRITICAL: Empty for v4
"css": "src/index.css",
"baseColor": "slate", // Base color palette
"cssVariables": true,
"prefix": "" // No prefix for utility classes
}
}
4. Delete tailwind.config.ts
rm tailwind.config.ts # v4 doesn't use this file
The Four-Step Architecture (CRITICAL)
This pattern is mandatory - skipping steps will break your theme.
Step 1: Define CSS Variables at Root Level
/* src/index.css */
@import "tailwindcss";
:root {
--background: hsl(0 0% 100%); /* ← hsl() wrapper required */
--foreground: hsl(222.2 84% 4.9%);
--primary: hsl(221.2 83.2% 53.3%);
/* ... all light mode colors */
}
.dark {
--background: hsl(222.2 84% 4.9%);
--foreground: hsl(210 40% 98%);
--primary: hsl(217.2 91.2% 59.8%);
/* ... all dark mode colors */
}
Critical Rules:
- ✅ Define at root level (NOT inside
@layer base) - ✅ Use
hsl()wrapper on all color values - ✅ Use
.darkfor dark mode (NOT.dark { @theme { } })
Step 2: Map Variables to Tailwind Utilities
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-primary: var(--primary);
/* ... map ALL CSS variables */
}
Why This Is Required:
- Generates utility classes (
bg-background,text-primary) - Without this,
bg-primaryetc. won't exist
Step 3: Apply Base Styles
@layer base {
body {
background-color: var(--background); /* NO hsl() here */
color: var(--foreground);
}
}
Critical Rules:
- ✅ Reference variables directly:
var(--background) - ❌ Never double-wrap:
hsl(var(--background))
Step 4: Result - Automatic Dark Mode
<div className="bg-background text-foreground">
{/* No dark: variants needed - theme switches automatically */}
</div>
Dark Mode Setup
1. Create ThemeProvider
See reference/dark-mode.md for full implementation or use template:
// Copy from: templates/theme-provider.tsx
2. Wrap Your App
// src/main.tsx
import { ThemeProvider } from '@/components/theme-provider'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
<App />
</ThemeProvider>
</React.StrictMode>,
)
3. Add Theme Toggle
pnpm dlx shadcn@latest add dropdown-menu
See reference/dark-mode.md for ModeToggle component code.
Critical Rules (MUST FOLLOW)
✅ Always Do:
-
Wrap color values with
hsl()in:rootand.dark--background: hsl(0 0% 100%); /* ✅ Correct */ -
Use
@theme inlineto map all CSS variables@theme inline { --color-background: var(--background); } -
Set
"tailwind.config": ""in components.json{ "tailwind": { "config": "" } } -
Delete
tailwind.config.tsif it exists -
Use
@tailwindcss/viteplugin (NOT PostCSS) -
Use
cn()for conditional classesimport { cn } from "@/lib/utils" <div className={cn("base", isActive && "active")} />
❌ Never Do:
-
Put
:rootor.darkinside@layer base/* WRONG */ @layer base { :root { --background: hsl(...); } } -
Use
.dark { @theme { } }pattern/* WRONG - v4 doesn't support nested @theme */ .dark { @theme { --color-primary: hsl(...); } } -
Double-wrap colors
/* WRONG */ body { background-color: hsl(var(--background)); } -
Use
tailwind.config.tsfor theme colors/* WRONG - v4 ignores this */ export default { theme: { extend: { colors: { primary: 'hsl(var(--primary))' } } } } -
Use
@applydirective (deprecated in v4) -
Use
dark:variants for semantic colors/* WRONG */ <div className="bg-primary dark:bg-primary-dark" /> /* CORRECT */ <div className="bg-primary" />
Semantic Color Tokens
Always use semantic names for colors:
:root {
--destructive: hsl(0 84.2% 60.2%); /* Red - errors, critical */
--success: hsl(142.1 76.2% 36.3%); /* Green - success states */
--warning: hsl(38 92% 50%); /* Yellow - warnings */
--info: hsl(221.2 83.2% 53.3%); /* Blue - info, primary */
}
Usage:
<div className="bg-destructive text-destructive-foreground">Critical</div>
<div className="bg-success text-success-foreground">Success</div>
<div className="bg-warning text-warning-foreground">Warning</div>
<div className="bg-info text-info-foreground">Info</div>
Common Issues & Quick Fixes
| Symptom | Cause | Fix |
|---|---|---|
bg-primary doesn't work | Missing @theme inline mapping | Add @theme inline block |
| Colors all black/white | Double hsl() wrapping | Use var(--color) not hsl(var(--color)) |
| Dark mode not switching | Missing ThemeProvider | Wrap app in <ThemeProvider> |
| Build fails | tailwind.config.ts exists | Delete the file |
| Text invisible | Wrong contrast colors | Check color definitions in :root/.dark |
See reference/common-gotchas.md for complete troubleshooting guide.
File Templates
All templates are available in the templates/ directory:
- index.css - Complete CSS setup with all color variables
- components.json - shadcn/ui v4 configuration
- vite.config.ts - Vite + Tailwind plugin setup
- tsconfig.app.json - TypeScript with path aliases
- theme-provider.tsx - Dark mode provider with localStorage
- utils.ts -
cn()utility for class merging
Copy these files to your project and customize as needed.
Complete Setup Checklist
- Vite + React + TypeScript project created
-
@tailwindcss/viteinstalled (NOT postcss) -
vite.config.tsusestailwindcss()plugin -
tsconfig.jsonhas path aliases configured -
components.jsonexists with"config": "" - NO
tailwind.config.tsfile exists -
src/index.cssfollows v4 pattern:-
:rootand.darkat root level (not in @layer) - Colors wrapped with
hsl() -
@theme inlinemaps all variables -
@layer baseuses unwrapped variables
-
- Theme provider installed and wrapping app
- Dark mode toggle component created
- Test theme switching works in browser
Advanced Topics
Custom Colors
Add new semantic colors:
:root {
--brand: hsl(280 65% 60%);
--brand-foreground: hsl(0 0% 100%);
}
.dark {
--brand: hsl(280 75% 70%);
--brand-foreground: hsl(280 20% 10%);
}
@theme inline {
--color-brand: var(--brand);
--color-brand-foreground: var(--brand-foreground);
}
Usage: <div className="bg-brand text-brand-foreground">Branded</div>
Migration from v3
See reference/migration-guide.md for complete v3 → v4 migration steps.
Component Best Practices
-
Always use semantic tokens
<Button variant="destructive">Delete</Button> /* ✅ */ <Button className="bg-red-600">Delete</Button> /* ❌ */ -
Use
cn()for conditional stylingimport { cn } from "@/lib/utils" <div className={cn( "base-class", isActive && "active-class", hasError && "error-class" )} /> -
Compose shadcn/ui components
<Dialog> <DialogTrigger asChild> <Button>Open</Button> </DialogTrigger> <DialogContent> <DialogHeader> <DialogTitle>Title</DialogTitle> </DialogHeader> </DialogContent> </Dialog>
Dependencies
✅ Install These
{
"dependencies": {
"tailwindcss": "^4.1.17",
"@tailwindcss/vite": "^4.1.17",
"clsx": "^2.1.1",
"tailwind-merge": "^3.3.1",
"@radix-ui/react-*": "latest",
"lucide-react": "^0.553.0",
"react": "^19.2.0",
"react-dom": "^19.2.0"
},
"devDependencies": {
"@types/node": "^24.10.0",
"@vitejs/plugin-react": "^5.1.0",
"vite": "^7.2.2",
"typescript": "~5.9.0",
"tw-animate-css": "^1.4.0"
}
}
Animation Packages (Updated Nov 2025)
shadcn/ui has deprecated tailwindcss-animate in favor of tw-animate-css for Tailwind v4 compatibility.
✅ DO Install (v4-compatible):
pnpm add -D tw-animate-css
Then add to src/index.css:
@import "tailwindcss";
@import "tw-animate-css";
❌ DO NOT Install:
npm install tailwindcss-animate # Deprecated - v3 only
Why: tw-animate-css is the official v4-compatible replacement for animations, required by shadcn/ui components.
Reference: https://ui.shadcn.com/docs/tailwind-v4
Tailwind v4 Plugins
Tailwind v4 supports official plugins using the @plugin directive in CSS.
Official Plugins (Tailwind Labs)
Typography Plugin - Style Markdown/CMS Content
When to use: Displaying blog posts, documentation, or any HTML from Markdown/CMS.
Installation:
pnpm add -D @tailwindcss/typography
Configuration (v4 syntax):
/* src/index.css */
@import "tailwindcss";
@plugin "@tailwindcss/typography";
Usage:
<article class="prose lg:prose-xl dark:prose-invert">
{{ markdown_content }}
</article>
Available classes:
prose- Base typography stylesprose-sm,prose-base,prose-lg,prose-xl,prose-2xl- Size variantsdark:prose-invert- Dark mode styles
Forms Plugin - Reset Form Element Styles
When to use: Building custom forms without shadcn/ui components, or need consistent cross-browser form styling.
Installation:
pnpm add -D @tailwindcss/forms
Configuration (v4 syntax):
/* src/index.css */
@import "tailwindcss";
@plugin "@tailwindcss/forms";
What it does:
- Resets browser default form styles
- Makes form elements styleable with Tailwind utilities
- Fixes cross-browser inconsistencies for inputs, selects, checkboxes, radios
Note: Less critical for shadcn/ui users (they have pre-styled form components), but still useful for basic forms.
Common Plugin Errors
These errors happen when using v3 syntax in v4 projects:
❌ WRONG (v3 config file syntax):
// tailwind.config.js
module.exports = {
plugins: [require('@tailwindcss/typography')]
}
❌ WRONG (@import instead of @plugin):
@import "@tailwindcss/typography"; /* Doesn't work */
✅ CORRECT (v4 @plugin directive):
/* src/index.css */
@import "tailwindcss";
@plugin "@tailwindcss/typography";
@plugin "@tailwindcss/forms";
Built-in Features (No Plugin Needed)
Container queries are built into Tailwind v4 core - no plugin needed:
<div className="@container">
<div className="@md:text-lg">
Responds to container width, not viewport
</div>
</div>
❌ Don't install: @tailwindcss/container-queries (deprecated, now core feature)
Reference Documentation
For deeper understanding, see:
- architecture.md - Deep dive into the 4-step pattern
- dark-mode.md - Complete dark mode implementation
- common-gotchas.md - All the ways it can break (and fixes)
- migration-guide.md - Migrating hardcoded colors to CSS variables
Official Documentation
- shadcn/ui Vite Setup: https://ui.shadcn.com/docs/installation/vite
- shadcn/ui Tailwind v4 Guide: https://ui.shadcn.com/docs/tailwind-v4
- shadcn/ui Dark Mode (Vite): https://ui.shadcn.com/docs/dark-mode/vite
- Tailwind v4 Docs: https://tailwindcss.com/docs
- shadcn/ui Theming: https://ui.shadcn.com/docs/theming
Production Example
This skill is based on the WordPress Auditor project:
- Live: https://wordpress-auditor.webfonts.workers.dev
- Stack: Vite + React 19 + Tailwind v4 + shadcn/ui + Cloudflare Workers
- Dark Mode: Full system/light/dark support
- Version: Tailwind v4.1.14 + shadcn/ui latest (Oct 2025)
All patterns in this skill have been validated in production.
Questions? Issues?
- Check
reference/common-gotchas.mdfirst - Verify all steps in the 4-step architecture
- Ensure
components.jsonhas"config": "" - Delete
tailwind.config.tsif it exists - Check official docs: https://ui.shadcn.com/docs/tailwind-v4
GitHub Repository
Related Skills
sglang
MetaSGLang is a high-performance LLM serving framework that specializes in fast, structured generation for JSON, regex, and agentic workflows using its RadixAttention prefix caching. It delivers significantly faster inference, especially for tasks with repeated prefixes, making it ideal for complex, structured outputs and multi-turn conversations. Choose SGLang over alternatives like vLLM when you need constrained decoding or are building applications with extensive prefix sharing.
evaluating-llms-harness
TestingThis Claude Skill runs the lm-evaluation-harness to benchmark LLMs across 60+ standardized academic tasks like MMLU and GSM8K. It's designed for developers to compare model quality, track training progress, or report academic results. The tool supports various backends including HuggingFace and vLLM models.
llamaguard
OtherLlamaGuard is Meta's 7-8B parameter model for moderating LLM inputs and outputs across six safety categories like violence and hate speech. It offers 94-95% accuracy and can be deployed using vLLM, Hugging Face, or Amazon SageMaker. Use this skill to easily integrate content filtering and safety guardrails into your AI applications.
langchain
MetaLangChain is a framework for building LLM applications using agents, chains, and RAG pipelines. It supports multiple LLM providers, offers 500+ integrations, and includes features like tool calling and memory management. Use it for rapid prototyping and deploying production systems like chatbots, autonomous agents, and question-answering services.
