关于 tailwind-theme-builder
用于配置 Tailwind v4 与 shadcn/ui 主题的技能,采用 CSS 变量并支持暗色模式。提供配置文件、ThemeProvider 和常见设置故障的排查说明。
主要功能
- 01使用 Vite 插件配置 Tailwind v4
- 02通过 @theme inline 映射 CSS 变量
- 03设置基于 ThemeProvider 的暗色模式
- 04应用语义颜色令牌
- 05排查常见主题配置问题
适用场景
- 01初始化带主题的 shadcn/ui 项目
- 02添加浅色、深色和系统主题模式
- 03修复工具类缺失或颜色切换失效
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jezweb/ claude-skills --skill tailwind-theme-builder安装后在 Claude Code 中输入/tailwind-theme-builder确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/components.jsonindex.csstheme-provider.tsxtsconfig.app.jsonutils.tsvite.config.tsreferences/migration-guide.md详细介绍
Tailwind 主题构建器
为 Tailwind v4 + shadcn/ui 项目设置完整主题,支持深色模式。生成配置好的 CSS、主题提供程序和可用的组件库。
架构:四步模式
Tailwind v4 对基于 CSS 变量的主题设置要求采用特定架构。此模式必须遵循——跳过或修改步骤都会导致主题失效。
工作原理
CSS Variable Definition --> @theme inline Mapping --> Tailwind Utility Class
--background --> --color-background --> bg-background
(with hsl() wrapper) (references variable) (generated class)
深色模式切换:
ThemeProvider toggles .dark class on <html>
--> CSS variables update automatically (.dark overrides :root)
--> Tailwind utilities reference updated variables
--> UI updates without re-render
最佳实践
- 语义化命名: 使用
--primary,而不是--blue-500 - 前景色配对: 每种背景色都需要对应的前景色(
--primary+--primary-foreground) - WCAG 对比度: 普通文本为 4.5:1,大号文本为 3:1,UI 组件为 3:1
- 图表颜色: 使用独立变量并通过
@theme inline映射,在样式属性中通过var(--chart-1)引用
工作流
第 1 步:安装依赖
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
第 2 步:配置 Vite
复制 assets/vite.config.ts,或添加 Tailwind 插件:
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 步:四步 CSS 架构(必须遵循)
必须严格按照此顺序操作。跳过步骤会导致主题失效。
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);
}
}
结果: bg-background、text-primary 等会自动生效。深色模式通过 .dark 类切换——语义化颜色无需使用 dark: 变体。
第 4 步:设置深色模式
将 assets/theme-provider.tsx 复制到组件目录,然后包裹应用:
import { ThemeProvider } from '@/components/theme-provider'
ReactDOM.createRoot(document.getElementById('root')!).render(
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
<App />
</ThemeProvider>
)
添加主题切换器——安装下拉菜单,然后使用下方的 ModeToggle 组件:
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>
)
}
第 5 步:配置 components.json
{
"tailwind": {
"config": "",
"css": "src/index.css",
"baseColor": "slate",
"cssVariables": true
}
}
"config": "" 至关重要——v4 不使用 tailwind.config.ts。
关键规则
始终:
- 在
:root/.dark中使用hsl()包裹颜色 - 使用
@theme inline映射所有 CSS 变量 - 使用
@tailwindcss/vite插件(而非 PostCSS) - 如果存在
tailwind.config.ts,请将其删除
切勿:
- 将
:root/.dark放在@layer base内 - 使用
.dark { @theme { } }(v4 不支持嵌套的 @theme) - 重复包裹:
hsl(var(--background)) - 将
@apply与@layer base类一起使用(改用@utility)
全部 18 个注意事项
快速诊断
| # | 症状 | 原因 | 修复方法 |
|---|---|---|---|
| 1 | 变量被忽略 / 主题失效 | :root 位于 @layer base 内 | 将 :root 和 .dark 移至根级别 |
| 2 | 深色模式颜色未切换 | .dark { @theme { } } | 使用 CSS 变量 + 单个 @theme inline |
| 3 | 颜色全变成黑色/白色 | 重复使用 hsl() 包装 | 使用 var(--background),而不是 hsl(var(...)) |
| 4 | 未生成 bg-primary | 颜色配置在 tailwind.config.ts 中 | 删除配置文件,改用 @theme inline |
| 5 | 缺少 bg-background 类 | 没有 @theme inline 块 | 添加 @theme inline 变量映射 |
| 6 | shadcn 组件失效 | components.json 中包含配置路径 | 将 "config": "" 设为空字符串 |
| 7 | Tailwind 未处理样式 | 使用了 PostCSS 插件 | 切换为 @tailwindcss/vite 插件 |
| 8 | @/ 导入失败 | 缺少路径别名 | 在 tsconfig.app.json 中添加 paths |
| 9 | dark: 变体冗余 | 使用 dark:bg-primary-dark | 直接使用 bg-primary——变量会自动处理 |
| 10 | 到处都是硬编码的颜色 | 使用 bg-blue-600 dark:bg-blue-400 | 使用语义化令牌:bg-primary |
| 11 | 类名合并错误 | 使用字符串拼接类名 | 使用 @/lib/utils 中的 cn() |
| 12 | Radix Select 崩溃 | 值为空字符串 value="" | 使用 value="placeholder" |
| 13 | Tailwind 版本错误 | 安装了 tailwindcss@^3 | 安装 tailwindcss@^4.1.0 + @tailwindcss/vite |
| 14 | 缺少 peer 依赖 | 只安装了 tailwindcss | 还要安装 clsx、tailwind-merge、@types/node |
| 15 | 深色模式下失效 | 只测试了浅色模式 | 测试浅色、深色、系统模式及模式切换 |
| 16 | 不符合 WCAG 对比度要求 | 视觉上看起来没问题 | 检查对比度:普通文本为 4.5:1,大号文本/UI 为 3:1 |
| 17 | 导入动画时构建失败 | 使用了已弃用的 tailwindcss-animate | 使用 tw-animate-css 或原生 CSS 动画 |
| 18 | CSS 优先级问题 | shadcn 初始化后出现重复的 @layer base | 合并为单个 @layer base 块 |
常见问题及代码示例
#1 -- @layer base 内的 :root
Tailwind v4 会移除 @theme/@layer 之外的 CSS,但 :root 必须位于根级才能保留。这是最常见的配置失败原因。
错误:
@layer base {
:root { --background: hsl(0 0% 100%); }
}
正确:
:root { --background: hsl(0 0% 100%); }
@layer base {
body { background-color: var(--background); }
}
#2 -- 嵌套的 @theme
Tailwind v4 不支持在选择器内使用 @theme。请在 :root/.dark 中使用 CSS 变量,并只使用一个 @theme inline 块。
错误:
@theme { --color-primary: hsl(0 0% 0%); }
.dark { @theme { --color-primary: hsl(0 0% 100%); } }
正确:
:root { --primary: hsl(0 0% 0%); }
.dark { --primary: hsl(0 0% 100%); }
@theme inline { --color-primary: var(--primary); }
#3 -- 重复包裹 hsl()
变量本身已包含 hsl()。重复包裹会生成 hsl(hsl(...))。
错误:background-color: hsl(var(--background));
正确:background-color: var(--background);
#4 -- tailwind.config.ts 中的颜色
Tailwind v4 会完全忽略配置文件中的 theme.extend.colors。请删除该文件或将其留空。在 components.json 中设置 "config": ""。
#5 -- 缺少 @theme inline
没有 @theme inline,Tailwind 就无法识别你的 CSS 变量。bg-background 之类的实用类不会生成。
错误:
:root { --background: hsl(0 0% 100%); }
/* No @theme inline block -- bg-background won't exist */
正确:
:root { --background: hsl(0 0% 100%); }
@theme inline { --color-background: var(--background); }
#7 -- PostCSS 与 Vite 插件
错误:
export default defineConfig({
css: { postcss: './postcss.config.js' } // Old v3 way
})
正确:
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [react(), tailwindcss()] // v4 way
})
#8 -- 路径别名
添加到 tsconfig.app.json:
{
"compilerOptions": {
"baseUrl": ".",
"paths": { "@/*": ["./src/*"] }
}
}
#11 -- 用于合并类名的 cn() 实用函数
错误:className={`base ${isActive && 'active'}`}
正确:className={cn("base", isActive && "active")}
@/lib/utils 中的 cn() 可以正确合并并去重 Tailwind 类。
#12 -- Radix Select 的空值
Radix UI Select 不允许空字符串值。请使用 value="placeholder",而不是 value=""。
#14 -- 必需的依赖项
{
"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 在 Tailwind v4 中已弃用。shadcn/ui 文档中可能仍会提及它。它会导致构建失败和导入错误。请改用 tw-animate-css 或 @tailwindcss/motion。
#18 -- shadcn init 后出现重复的 @layer base
shadcn init 会添加自己的 @layer base 块。运行 init 后立即检查 src/index.css,并将重复的块合并为一个。
错误:
@layer base { body { background-color: var(--background); } }
@layer base { * { border-color: hsl(var(--border)); } } /* duplicate from shadcn */
正确:
@layer base {
* { border-color: var(--border); }
body { background-color: var(--background); color: var(--foreground); }
}
预防检查清单
- 没有
tailwind.config.ts文件(或文件为空) -
components.json中有"config": "" - 所有颜色在
:root中都有hsl()包裹 -
@theme inline映射了所有变量 -
@layer base没有包裹:root - 主题提供程序包裹了应用
- 已在浅色、深色和系统模式下测试
- 所有文本的对比度都足够
深色模式测试清单
- 浅色模式显示正常
- 深色模式显示正常
- 系统模式遵循操作系统设置
- 页面刷新后主题仍会保留
- 切换组件会显示当前状态
- 所有文本都有合适的对比度
- 加载时不会短暂显示错误的主题
- 在无痕模式下正常工作(可妥善回退)
资源文件
从 assets/ 目录复制:
index.css-- 包含所有颜色变量的完整 CSScomponents.json-- shadcn/ui v4 配置vite.config.ts-- Vite + Tailwind 插件theme-provider.tsx-- 深色模式提供程序utils.ts--cn()实用函数
参考文件
references/migration-guide.md-- 从 v3 迁移到 v4
官方文档
- shadcn/ui Tailwind v4 指南:https://ui.shadcn.com/docs/tailwind-v4
- shadcn/ui 深色模式(Vite):https://ui.shadcn.com/docs/dark-mode/vite
- shadcn/ui 主题设置:https://ui.shadcn.com/docs/theming
- Tailwind v4 文档:https://tailwindcss.com/docs
- Tailwind 深色模式:https://tailwindcss.com/docs/dark-mode