mcpField
T
tailwind-theme-builder

tailwind-theme-builder

github.com/jezweb设计与 UI1.1k3.2k 次安装3 周前更新
代码仓库

关于 tailwind-theme-builder

用于配置 Tailwind v4 与 shadcn/ui 主题的技能,采用 CSS 变量并支持暗色模式。提供配置文件、ThemeProvider 和常见设置故障的排查说明。

主要功能

  1. 01使用 Vite 插件配置 Tailwind v4
  2. 02通过 @theme inline 映射 CSS 变量
  3. 03设置基于 ThemeProvider 的暗色模式
  4. 04应用语义颜色令牌
  5. 05排查常见主题配置问题

适用场景

  1. 01初始化带主题的 shadcn/ui 项目
  2. 02添加浅色、深色和系统主题模式
  3. 03修复工具类缺失或颜色切换失效

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jezweb/claude-skills --skill tailwind-theme-builder

安装后在 Claude Code 中输入/tailwind-theme-builder确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

assets/
components.json
index.css
theme-provider.tsx
tsconfig.app.json
utils.ts
vite.config.ts
references/
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 变量映射
6shadcn 组件失效components.json 中包含配置路径将 "config": "" 设为空字符串
7Tailwind 未处理样式使用了 PostCSS 插件切换为 @tailwindcss/vite 插件
8@/ 导入失败缺少路径别名在 tsconfig.app.json 中添加 paths
9dark: 变体冗余使用 dark:bg-primary-dark直接使用 bg-primary——变量会自动处理
10到处都是硬编码的颜色使用 bg-blue-600 dark:bg-blue-400使用语义化令牌:bg-primary
11类名合并错误使用字符串拼接类名使用 @/lib/utils 中的 cn()
12Radix Select 崩溃值为空字符串 value=""使用 value="placeholder"
13Tailwind 版本错误安装了 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 动画
18CSS 优先级问题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 -- 包含所有颜色变量的完整 CSS
  • components.json -- shadcn/ui v4 配置
  • vite.config.ts -- Vite + Tailwind 插件
  • theme-provider.tsx -- 深色模式提供程序
  • utils.ts -- cn() 实用函数

参考文件

  • references/migration-guide.md -- 从 v3 迁移到 v4

官方文档

评论

支持 Markdown