470 lines
7.4 KiB
Markdown
470 lines
7.4 KiB
Markdown
# Common Gotchas & Solutions
|
|
|
|
## Critical Failures (Will Break Your Build)
|
|
|
|
### 1. `:root` Inside `@layer base`
|
|
|
|
❌ **WRONG:**
|
|
```css
|
|
@layer base {
|
|
:root {
|
|
--background: hsl(0 0% 100%);
|
|
}
|
|
}
|
|
```
|
|
|
|
✅ **CORRECT:**
|
|
```css
|
|
: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:**
|
|
```css
|
|
@theme {
|
|
--color-primary: hsl(0 0% 0%);
|
|
}
|
|
|
|
.dark {
|
|
@theme {
|
|
--color-primary: hsl(0 0% 100%);
|
|
}
|
|
}
|
|
```
|
|
|
|
✅ **CORRECT:**
|
|
```css
|
|
: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:**
|
|
```css
|
|
@layer base {
|
|
body {
|
|
background-color: hsl(var(--background));
|
|
}
|
|
}
|
|
```
|
|
|
|
✅ **CORRECT:**
|
|
```css
|
|
@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:**
|
|
```typescript
|
|
// tailwind.config.ts
|
|
export default {
|
|
theme: {
|
|
extend: {
|
|
colors: {
|
|
primary: 'hsl(var(--primary))'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
✅ **CORRECT:**
|
|
```typescript
|
|
// 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:**
|
|
```css
|
|
:root {
|
|
--background: hsl(0 0% 100%);
|
|
}
|
|
|
|
/* No @theme inline block */
|
|
```
|
|
|
|
Result: `bg-background` class doesn't exist
|
|
|
|
✅ **CORRECT:**
|
|
```css
|
|
: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:**
|
|
```json
|
|
{
|
|
"tailwind": {
|
|
"config": "tailwind.config.ts" // ← No!
|
|
}
|
|
}
|
|
```
|
|
|
|
✅ **CORRECT:**
|
|
```json
|
|
{
|
|
"tailwind": {
|
|
"config": "" // ← Empty for v4
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 7. Using PostCSS Instead of Vite Plugin
|
|
|
|
❌ **WRONG:**
|
|
```typescript
|
|
// vite.config.ts
|
|
export default defineConfig({
|
|
css: {
|
|
postcss: './postcss.config.js' // Old v3 way
|
|
}
|
|
})
|
|
```
|
|
|
|
✅ **CORRECT:**
|
|
```typescript
|
|
import tailwindcss from '@tailwindcss/vite'
|
|
|
|
export default defineConfig({
|
|
plugins: [react(), tailwindcss()] // v4 way
|
|
})
|
|
```
|
|
|
|
---
|
|
|
|
### 8. Missing Path Aliases
|
|
|
|
❌ **WRONG:**
|
|
```typescript
|
|
// tsconfig.json has no paths
|
|
import { Button } from '../../components/ui/button'
|
|
```
|
|
|
|
✅ **CORRECT:**
|
|
```json
|
|
// tsconfig.app.json
|
|
{
|
|
"compilerOptions": {
|
|
"baseUrl": ".",
|
|
"paths": {
|
|
"@/*": ["./src/*"]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
```typescript
|
|
import { Button } from '@/components/ui/button'
|
|
```
|
|
|
|
---
|
|
|
|
## Color System Gotchas
|
|
|
|
### 9. Using `dark:` Variants for Semantic Colors
|
|
|
|
❌ **WRONG:**
|
|
```tsx
|
|
<div className="bg-primary dark:bg-primary-dark" />
|
|
```
|
|
|
|
✅ **CORRECT:**
|
|
```tsx
|
|
<div className="bg-primary" />
|
|
```
|
|
|
|
**Why:** With proper CSS variable setup, `bg-primary` automatically responds to theme.
|
|
|
|
---
|
|
|
|
### 10. Hardcoded Color Values
|
|
|
|
❌ **WRONG:**
|
|
```tsx
|
|
<div className="bg-blue-600 dark:bg-blue-400" />
|
|
```
|
|
|
|
✅ **CORRECT:**
|
|
```tsx
|
|
<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:**
|
|
```tsx
|
|
<div className={`base ${isActive && 'active'}`} />
|
|
```
|
|
|
|
✅ **CORRECT:**
|
|
```tsx
|
|
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:**
|
|
```tsx
|
|
<SelectItem value="">Select an option</SelectItem>
|
|
```
|
|
|
|
✅ **CORRECT:**
|
|
```tsx
|
|
<SelectItem value="placeholder">Select an option</SelectItem>
|
|
```
|
|
|
|
**Why:** Radix UI Select doesn't allow empty string values.
|
|
|
|
---
|
|
|
|
## Installation Gotchas
|
|
|
|
### 13. Wrong Tailwind Package
|
|
|
|
❌ **WRONG:**
|
|
```bash
|
|
npm install tailwindcss@^3.4.0 # v3
|
|
```
|
|
|
|
✅ **CORRECT:**
|
|
```bash
|
|
npm install tailwindcss@^4.1.0 # v4
|
|
npm install @tailwindcss/vite
|
|
```
|
|
|
|
---
|
|
|
|
### 14. Missing Dependencies
|
|
|
|
❌ **WRONG:**
|
|
```json
|
|
{
|
|
"dependencies": {
|
|
"tailwindcss": "^4.1.0"
|
|
// Missing @tailwindcss/vite
|
|
}
|
|
}
|
|
```
|
|
|
|
✅ **CORRECT:**
|
|
```json
|
|
{
|
|
"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:**
|
|
```bash
|
|
npm install tailwindcss-animate # Deprecated package
|
|
```
|
|
|
|
```css
|
|
@import "tw-animate-css"; # Package doesn't exist in v4
|
|
```
|
|
|
|
✅ **CORRECT:**
|
|
```bash
|
|
# Don't install tailwindcss-animate at all
|
|
# Use native CSS animations or @tailwindcss/motion
|
|
```
|
|
|
|
**Why:**
|
|
- `tailwindcss-animate` is 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:**
|
|
```css
|
|
/* 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:**
|
|
```css
|
|
/* Merge into single @layer base block */
|
|
@layer base {
|
|
* {
|
|
border-color: var(--border);
|
|
}
|
|
|
|
body {
|
|
background-color: var(--background);
|
|
color: var(--foreground);
|
|
}
|
|
}
|
|
```
|
|
|
|
**Why:**
|
|
- `shadcn init` adds its own `@layer base` block
|
|
- Results in duplicate layer declarations
|
|
- Can cause unexpected CSS priority issues
|
|
- Easy to miss during setup
|
|
|
|
**Prevention:**
|
|
- Check `src/index.css` immediately after running `shadcn init`
|
|
- Merge any duplicate `@layer base` blocks
|
|
- 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.ts` file (or it's empty)
|
|
- [ ] `components.json` has `"config": ""`
|
|
- [ ] All colors have `hsl()` wrapper in `:root`
|
|
- [ ] `@theme inline` maps all variables
|
|
- [ ] `@layer base` doesn't wrap `:root`
|
|
- [ ] Theme provider wraps app
|
|
- [ ] Tested in both light and dark modes
|
|
- [ ] All text has sufficient contrast
|