name: Tailwind v4 + shadcn/ui description: | Set up Tailwind v4 with shadcn/ui using @theme inline pattern and CSS variable architecture. Includes component composition patterns, accessibility guidelines, and React Hook Form integration.
Use when: initializing React projects with Tailwind v4, setting up shadcn/ui dark mode, composing/extending components, implementing forms, ensuring accessibility, or fixing colors not working, theme not applying, CSS variables broken, tw-animate-css errors, or migrating from v3.
Skill: shadcn
Scope
- Applies to: Tailwind v4 with shadcn/ui setup, CSS variable architecture, dark mode, theme configuration, component composition patterns, accessibility, form integration
- Does NOT cover: Tailwind v3, PostCSS configuration
Assumptions
- Tailwind CSS v4+
- shadcn/ui latest
- Vite (use
@tailwindcss/viteplugin) - React 18+ or Next.js 14+
- TypeScript 5+
Principles
- Use
@theme inlineto map CSS variables to Tailwind tokens - Use
hsl()wrapper for color values in:rootand.dark - Set
"tailwind.config": ""incomponents.json(empty for v4) - Delete
tailwind.config.tsif it exists (v4 uses CSS-based config) - Use
@tailwindcss/viteplugin (not PostCSS) - Use
cn()utility for conditional classes - Semantic colors automatically adapt to dark mode (no
dark:variants needed) - Use
@plugindirective for plugins (not@importorrequire()) - Compose complex components from smaller shadcn primitives
- Extend components via wrapper pattern (don't modify originals)
- Use CVA (class-variance-authority) for variant systems
- Always use
forwardReffor form-compatible components - Leverage Radix UI primitives for built-in accessibility
Constraints
MUST
- Wrap color values with
hsl()in:rootand.dark - Use
@theme inlineto map all CSS variables - Set
"tailwind.config": ""incomponents.json - Delete
tailwind.config.tsif it exists - Use
@tailwindcss/viteplugin
SHOULD
- Use semantic color tokens (
--background,--foreground, etc.) - Use
cn()utility for conditional classes - Use
@plugindirective for plugins - Compose components from smaller shadcn primitives
- Use wrapper pattern to extend components (don't modify originals)
- Use CVA for variant systems in custom components
- Use
forwardReffor components that need ref forwarding - Test accessibility with keyboard navigation and screen readers
- Use Radix UI primitives for complex interactions (dialogs, dropdowns, etc.)
- Provide ARIA labels for icon-only buttons and interactive elements
AVOID
- Putting
:rootor.darkinside@layer base - Using
.dark { @theme { } }pattern (v4 doesn't support nested @theme) - Double-wrapping colors (
hsl(var(--background))in body) - Using
tailwind.config.tsfor theme colors - Using
@applydirective (deprecated in v4) - Using
dark:variants for semantic colors - Using
@importorrequire()for plugins (use@plugin) - Modifying base shadcn components directly (use wrapper pattern)
- Building custom dropdowns/dialogs from scratch (use Radix primitives)
- Relying on color alone for state indication
- Skipping accessibility testing
Interactions
- Works with nextjs for App Router setup
- Uses typescript for type safety
Patterns
CSS Variable Setup
/* src/index.css */
@import "tailwindcss";
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
}
:root {
--background: hsl(0 0% 100%);
--foreground: hsl(222.2 84% 4.9%);
}
.dark {
--background: hsl(222.2 84% 4.9%);
--foreground: hsl(210 40% 98%);
}
Vite Configuration
// vite.config.ts
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [react(), tailwindcss()],
})
Components Config
{
"tailwind": {
"config": "",
"css": "src/index.css",
"baseColor": "slate",
"cssVariables": true
}
}
See Templates (including Component Extension) and Architecture for complete setup.
References
- Architecture - Complete setup pattern
- Dark Mode - Dark mode implementation
- Common Gotchas - Common issues and fixes
- Migration Guide - Migrating from v3
- Component Patterns - Composition, CVA, extension patterns
- Accessibility - ARIA, keyboard navigation, screen readers
- Form Patterns - React Hook Form integration