7.4 KiB
Common Gotchas & Solutions
Critical Failures (Will Break Your Build)
1. :root Inside @layer base
❌ WRONG:
@layer base {
:root {
--background: hsl(0 0% 100%);
}
}
✅ CORRECT:
:root {
--background: hsl(0 0% 100%);
}
@layer base {
body {
background-color: var(--background);
}
}
Why: Tailwind v4 strips CSS outside @theme/@layer, but :root must be at root level.
2. Nested @theme Directive
❌ WRONG:
@theme {
--color-primary: hsl(0 0% 0%);
}
.dark {
@theme {
--color-primary: hsl(0 0% 100%);
}
}
✅ CORRECT:
:root {
--primary: hsl(0 0% 0%);
}
.dark {
--primary: hsl(0 0% 100%);
}
@theme inline {
--color-primary: var(--primary);
}
Why: Tailwind v4 doesn't support @theme inside selectors.
3. Double hsl() Wrapping
❌ WRONG:
@layer base {
body {
background-color: hsl(var(--background));
}
}
✅ CORRECT:
@layer base {
body {
background-color: var(--background); /* Already has hsl() */
}
}
Why: Variables already contain hsl(), double-wrapping creates hsl(hsl(...)).
4. Colors in tailwind.config.ts
❌ WRONG:
// tailwind.config.ts
export default {
theme: {
extend: {
colors: {
primary: 'hsl(var(--primary))'
}
}
}
}
✅ CORRECT:
// Delete tailwind.config.ts entirely OR leave it empty
export default {}
// components.json
{
"tailwind": {
"config": "" // ← Empty string
}
}
Why: Tailwind v4 completely ignores theme.extend.colors.
5. Missing @theme inline Mapping
❌ WRONG:
:root {
--background: hsl(0 0% 100%);
}
/* No @theme inline block */
Result: bg-background class doesn't exist
✅ CORRECT:
:root {
--background: hsl(0 0% 100%);
}
@theme inline {
--color-background: var(--background);
}
Why: @theme inline generates the utility classes.
Configuration Gotchas
6. Wrong components.json Config
❌ WRONG:
{
"tailwind": {
"config": "tailwind.config.ts" // ← No!
}
}
✅ CORRECT:
{
"tailwind": {
"config": "" // ← Empty for v4
}
}
7. Using PostCSS Instead of Vite Plugin
❌ WRONG:
// vite.config.ts
export default defineConfig({
css: {
postcss: './postcss.config.js' // Old v3 way
}
})
✅ CORRECT:
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [react(), tailwindcss()] // v4 way
})
8. Missing Path Aliases
❌ WRONG:
// tsconfig.json has no paths
import { Button } from '../../components/ui/button'
✅ CORRECT:
// tsconfig.app.json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}
import { Button } from '@/components/ui/button'
Color System Gotchas
9. Using dark: Variants for Semantic Colors
❌ WRONG:
<div className="bg-primary dark:bg-primary-dark" />
✅ CORRECT:
<div className="bg-primary" />
Why: With proper CSS variable setup, bg-primary automatically responds to theme.
10. Hardcoded Color Values
❌ WRONG:
<div className="bg-blue-600 dark:bg-blue-400" />
✅ CORRECT:
<div className="bg-primary" /> {/* Or bg-info, bg-success, etc. */}
Why: Semantic tokens enable theme switching and reduce repetition.
Component Gotchas
11. Missing cn() Utility
❌ WRONG:
<div className={`base ${isActive && 'active'}`} />
✅ CORRECT:
import { cn } from '@/lib/utils'
<div className={cn("base", isActive && "active")} />
Why: cn() properly merges and deduplicates Tailwind classes.
12. Empty String in Radix Select
❌ WRONG:
<SelectItem value="">Select an option</SelectItem>
✅ CORRECT:
<SelectItem value="placeholder">Select an option</SelectItem>
Why: Radix UI Select doesn't allow empty string values.
Installation Gotchas
13. Wrong Tailwind Package
❌ WRONG:
npm install tailwindcss@^3.4.0 # v3
✅ CORRECT:
npm install tailwindcss@^4.1.0 # v4
npm install @tailwindcss/vite
14. Missing Dependencies
❌ WRONG:
{
"dependencies": {
"tailwindcss": "^4.1.0"
// Missing @tailwindcss/vite
}
}
✅ CORRECT:
{
"dependencies": {
"tailwindcss": "^4.1.0",
"@tailwindcss/vite": "^4.1.0",
"clsx": "^2.1.1",
"tailwind-merge": "^3.3.1"
},
"devDependencies": {
"@types/node": "^24.0.0"
}
}
17. tw-animate-css Import Error (REAL-WORLD ISSUE)
❌ WRONG:
npm install tailwindcss-animate # Deprecated package
@import "tw-animate-css"; # Package doesn't exist in v4
✅ CORRECT:
# Don't install tailwindcss-animate at all
# Use native CSS animations or @tailwindcss/motion
Why:
tailwindcss-animateis deprecated in Tailwind v4- Causes import errors during build
- shadcn/ui docs may still reference it (outdated)
- The skill handles animations differently in v4
Impact: Build failure, requires manual CSS file cleanup
18. Duplicate @layer base After shadcn init (REAL-WORLD ISSUE)
❌ WRONG:
/* After running shadcn init, you might have: */
@layer base {
body {
background-color: var(--background);
}
}
@layer base { /* ← Duplicate added by shadcn init */
* {
border-color: hsl(var(--border));
}
}
✅ CORRECT:
/* Merge into single @layer base block */
@layer base {
* {
border-color: var(--border);
}
body {
background-color: var(--background);
color: var(--foreground);
}
}
Why:
shadcn initadds its own@layer baseblock- Results in duplicate layer declarations
- Can cause unexpected CSS priority issues
- Easy to miss during setup
Prevention:
- Check
src/index.cssimmediately after runningshadcn init - Merge any duplicate
@layer baseblocks - Keep only one base layer section
Impact: CSS priority issues, harder to debug styling problems
Testing Gotchas
15. Not Testing Both Themes
❌ WRONG: Only testing in light mode
✅ CORRECT: Test in:
- Light mode
- Dark mode
- System mode
- Both initial load and toggle
16. Not Checking Contrast
❌ WRONG: Colors look good but fail WCAG
✅ CORRECT:
- Use browser DevTools Lighthouse
- Check contrast ratios (4.5:1 minimum)
- Test with actual users
Quick Diagnosis
Symptoms → Likely Cause:
| Symptom | Likely Cause |
|---|---|
bg-primary doesn't work |
Missing @theme inline mapping |
| Colors all black/white | Double hsl() wrapping |
| Dark mode not switching | Missing ThemeProvider |
| Build fails | tailwind.config.ts exists with theme config |
| Text invisible | Wrong contrast colors |
@/ imports fail |
Missing path aliases in tsconfig |
Prevention Checklist
Before deploying:
- No
tailwind.config.tsfile (or it's empty) components.jsonhas"config": ""- All colors have
hsl()wrapper in:root @theme inlinemaps all variables@layer basedoesn't wrap:root- Theme provider wraps app
- Tested in both light and dark modes
- All text has sufficient contrast