‹ 首页

tailwind-theme-builder

@jezweb · 收录于 1 周前 · 上游提交 2 周前

Set up Tailwind v4 + shadcn/ui themed UI with dark mode. Install deps, configure CSS variables via @theme inline, wire dark mode toggle, verify. Use whenever the user mentions Tailwind v4, setting up Tailwind theming, shadcn/ui colours, dark mode, or troubleshooting colours not working, tw-animate-css errors, @theme inline conflicts, @apply breaking after upgrade, or v3 → v4 migration issues.

适合你,如果正在用 Tailwind v4 和 shadcn/ui 搭建 UI 主题。

/ 下载安装
tailwind-theme-builder.skill双击,或拖进 Claude 桌面版 / Cowork,即完成安装↓ .skill↓ .zip
用别的 agent?下载 .zip 解压,把文件夹放进它的技能目录
Claude Code~/.claude/skills/(项目级 .claude/skills/)
Codex CLI~/.codex/skills/
Cursor自动读取上面两处目录
其他工具见其文档的「skills」目录;两个下载是同一份文件,只是名字不同
/ 通过 npx 安装 校验哈希
npx oh-my-skill add jezweb/claude-skills/tailwind-theme-builder
/ 通过 bash 安装
curl -fsSL https://oh-my-skill.com/install.sh | bash -s -- jezweb/claude-skills/tailwind-theme-builder
/ 已经装过?验证本机副本,不用重装
npx oh-my-skill verify jezweb/claude-skills/tailwind-theme-builder
安装目标可用 --agent / --scope 或 --to 明确指定;省略时只会在唯一已存在的 agent 目录上自动选择,零命中或多命中会停止并提示。content_hash 缺失或不一致均拒装。
910GitHub stars
~2.8K最小装载
~4.7K含声明引用
~6.1K文本包总量
镜像托管

怎么用

技能原文 SKILL.md作者撰写 · MIT · e875a6b

Tailwind Theme Builder

Set up a fully themed Tailwind v4 + shadcn/ui project with dark mode. Produces configured CSS, theme provider, and working component library.

Architecture: The Four-Step Pattern

Tailwind v4 requires a specific architecture for CSS variable-based theming. This pattern is mandatory -- skipping or modifying steps breaks the theme.

How It Works
CSS Variable Definition --> @theme inline Mapping --> Tailwind Utility Class
--background           --> --color-background     --> bg-background
(with hsl() wrapper)      (references variable)     (generated class)

Dark mode switching:

ThemeProvider toggles .dark class on <html>
  --> CSS variables update automatically (.dark overrides :root)
  --> Tailwind utilities reference updated variables
  --> UI updates without re-render
Best Practices
  • Semantic names: Use --primary not --blue-500
  • Foreground pairing: Every background colour needs a foreground (--primary + --primary-foreground)
  • WCAG contrast: Normal text 4.5:1, large text 3:1, UI components 3:1
  • Chart colours: Use separate variables with @theme inline mapping, reference via var(--chart-1) in style props

Workflow
Step 1: Install Dependencies
pnpm add tailwindcss @tailwindcss/vite
pnpm add -D @types/node tw-animate-css
pnpm dlx shadcn@latest init

# Delete v3 config if it exists
rm -f tailwind.config.ts
Step 2: Configure Vite

Copy assets/vite.config.ts or add the Tailwind plugin:

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') } }
})
Step 3: Four-Step CSS Architecture (Mandatory)

This exact order is required. Skipping steps breaks the theme.

src/index.css:

@import "tailwindcss";
@import "tw-animate-css";

/* 1. Define CSS variables at root (NOT inside @layer base) */
: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 semantic tokens */
}

.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%);
}

/* 2. Map variables to Tailwind utilities */
@theme inline {
  --color-background: var(--background);
  --color-foreground: var(--foreground);
  --color-primary: var(--primary);
  --color-primary-foreground: var(--primary-foreground);
}

/* 3. Apply base styles (NO hsl() wrapper here) */
@layer base {
  body {
    background-color: var(--background);
    color: var(--foreground);
  }
}

Result: bg-background, text-primary etc. work automatically. Dark mode switches via .dark class -- no dark: variants needed for semantic colours.

Step 4: Set Up Dark Mode

Copy assets/theme-provider.tsx to your components directory, then wrap your app:

import { ThemeProvider } from '@/components/theme-provider'

ReactDOM.createRoot(document.getElementById('root')!).render(
  <ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
    <App />
  </ThemeProvider>
)

Add a theme toggle -- install the dropdown menu then use the ModeToggle component below:

pnpm dlx shadcn@latest add dropdown-menu
// src/components/mode-toggle.tsx
import { Moon, Sun } from "lucide-react"
import { Button } from "@/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { useTheme } from "@/components/theme-provider"

export function ModeToggle() {
  const { setTheme } = useTheme()

  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <Button variant="outline" size="icon">
          <Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
          <Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
          <span className="sr-only">Toggle theme</span>
        </Button>
      </DropdownMenuTrigger>
      <DropdownMenuContent align="end">
        <DropdownMenuItem onClick={() => setTheme("light")}>Light</DropdownMenuItem>
        <DropdownMenuItem onClick={() => setTheme("dark")}>Dark</DropdownMenuItem>
        <DropdownMenuItem onClick={() => setTheme("system")}>System</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  )
}
Step 5: Configure components.json
{
  "tailwind": {
    "config": "",
    "css": "src/index.css",
    "baseColor": "slate",
    "cssVariables": true
  }
}

"config": "" is critical -- v4 doesn't use tailwind.config.ts.


Critical Rules

Always:

  • Wrap colours with hsl() in :root/.dark
  • Use @theme inline to map all CSS variables
  • Use @tailwindcss/vite plugin (NOT PostCSS)
  • Delete tailwind.config.ts if it exists

Never:

  • Put :root/.dark inside @layer base
  • Use .dark { @theme { } } (v4 doesn't support nested @theme)
  • Double-wrap: hsl(var(--background))
  • Use @apply with @layer base classes (use @utility instead)

All 18 Gotchas
Quick Diagnosis

| # | Symptom | Cause | Fix | |---|---------|-------|-----| | 1 | Variables ignored / theme broken | :root inside @layer base | Move :root and .dark to root level | | 2 | Dark mode colours not switching | .dark { @theme { } } | Use CSS variables + single @theme inline | | 3 | Colours all black/white | Double hsl() wrapping | Use var(--background) not hsl(var(...)) | | 4 | bg-primary not generated | Colours in tailwind.config.ts | Delete config, use @theme inline | | 5 | bg-background class missing | No @theme inline block | Add @theme inline mapping variables | | 6 | shadcn components break | components.json has config path | Set "config": "" (empty string) | | 7 | Tailwind not processing | Using PostCSS plugin | Switch to @tailwindcss/vite plugin | | 8 | @/ imports fail | Missing path aliases | Add paths to tsconfig.app.json | | 9 | Redundant dark: variants | Using dark:bg-primary-dark | Just use bg-primary -- variables handle it | | 10 | Hardcoded colours everywhere | Using bg-blue-600 dark:bg-blue-400 | Use semantic tokens: bg-primary | | 11 | Class merging bugs | String concatenation for classes | Use cn() from @/lib/utils | | 12 | Radix Select crashes | Empty string value value="" | Use value="placeholder" | | 13 | Wrong Tailwind version | Installed tailwindcss@^3 | Install tailwindcss@^4.1.0 + @tailwindcss/vite | | 14 | Missing peer deps | Only installed tailwindcss | Also install clsx, tailwind-merge, @types/node | | 15 | Broken in dark mode | Only tested light mode | Test light, dark, system, and toggle transitions | | 16 | Fails WCAG contrast | Looks fine visually | Check ratios: 4.5:1 normal text, 3:1 large/UI | | 17 | Build fails on animation import | Using tailwindcss-animate (deprecated) | Use tw-animate-css or native CSS animations | | 18 | CSS priority issues | Duplicate @layer base after shadcn init | Merge into single @layer base block |

Gotcha Details with Code Examples

#1 -- :root inside @layer base

Tailwind v4 strips CSS outside @theme/@layer, but :root must be at root level to persist. This is the most common setup failure.

WRONG:

@layer base {
  :root { --background: hsl(0 0% 100%); }
}

CORRECT:

:root { --background: hsl(0 0% 100%); }
@layer base {
  body { background-color: var(--background); }
}

#2 -- Nested @theme

Tailwind v4 does not support @theme inside selectors. Use CSS variables in :root/.dark with a single @theme inline block.

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); }

#3 -- Double hsl() wrapping

Variables already contain hsl(). Double-wrapping creates hsl(hsl(...)).

WRONG: background-color: hsl(var(--background)); CORRECT: background-color: var(--background);

#4 -- Colours in tailwind.config.ts

Tailwind v4 completely ignores theme.extend.colors in config files. Delete the file or leave it empty. Set "config": "" in components.json.

#5 -- Missing @theme inline

Without @theme inline, Tailwind has no knowledge of your CSS variables. Utility classes like bg-background simply won't be generated.

WRONG:

:root { --background: hsl(0 0% 100%); }
/* No @theme inline block -- bg-background won't exist */

CORRECT:

:root { --background: hsl(0 0% 100%); }
@theme inline { --color-background: var(--background); }

#7 -- PostCSS vs Vite plugin

WRONG:

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 -- Path aliases

Add to tsconfig.app.json:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": { "@/*": ["./src/*"] }
  }
}

#11 -- cn() utility for class merging

WRONG: ` className={base ${isActive && 'active'}} CORRECT: className={cn("base", isActive && "active")}`

cn() from @/lib/utils properly merges and deduplicates Tailwind classes.

#12 -- Radix Select empty value

Radix UI Select does not allow empty string values. Use value="placeholder" instead of value="".

#14 -- Required dependencies

{
  "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

tailwindcss-animate is deprecated in Tailwind v4. shadcn/ui docs may still reference it. Causes build failures and import errors. Use tw-animate-css or @tailwindcss/motion instead.

#18 -- Duplicate @layer base after shadcn init

shadcn init adds its own @layer base block. Check src/index.css immediately after running init and merge any duplicate blocks into one.

WRONG:

@layer base { body { background-color: var(--background); } }
@layer base { * { border-color: hsl(var(--border)); } }  /* duplicate from shadcn */

CORRECT:

@layer base {
  * { border-color: var(--border); }
  body { background-color: var(--background); color: var(--foreground); }
}
Prevention Checklist
  • [ ] 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 light, dark, and system modes
  • [ ] All text has sufficient contrast

Dark Mode Testing Checklist
  • [ ] Light mode displays correctly
  • [ ] Dark mode displays correctly
  • [ ] System mode respects OS setting
  • [ ] Theme persists after page refresh
  • [ ] Toggle component shows current state
  • [ ] All text has proper contrast
  • [ ] No flash of wrong theme on load
  • [ ] Works in incognito mode (graceful fallback)

Asset Files

Copy from assets/ directory:

  • index.css -- Complete CSS with all colour variables
  • components.json -- shadcn/ui v4 config
  • vite.config.ts -- Vite + Tailwind plugin
  • theme-provider.tsx -- Dark mode provider
  • utils.ts -- cn() utility
Reference Files
  • references/migration-guide.md -- v3 to v4 migration
Official Documentation
  • 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
  • shadcn/ui Theming: https://ui.shadcn.com/docs/theming
  • Tailwind v4 Docs: https://tailwindcss.com/docs
  • Tailwind Dark Mode: https://tailwindcss.com/docs/dark-mode
按 MIT 许可原样转载,未经改动 · 在 GitHub 查看 →

评论

登录即可评论;带「已验证安装」的,是发布者名下有本店的安装或持有记录。