ui-ux-pro-max-skill ui-styling 实战指南:shadcn/ui 主题定制、CSS 变量与暗色模式实现
2026/9/5 16:22:23 网站建设 项目流程

ui-ux-pro-max-skill ui-styling 实战指南:shadcn/ui 主题定制、CSS 变量与暗色模式实现

【免费下载链接】ui-ux-pro-max-skillAn AI skill that provides design intelligence for building professional UI/UX across multiple platforms.项目地址: https://gitcode.com/gh_mirrors/ui/ui-ux-pro-max-skill

本文基于 ui-ux-pro-max-skill 仓库中 ui-styling 技能的主题参考文档 shadcn-theming.md,系统讲解 shadcn/ui 的主题体系:暗色模式接入、CSS 变量令牌系统、Tailwind 映射配置、颜色/圆角定制与组件变体扩展。读完后,你可以直接在一套 React 应用中落地完整的 shadcn/ui 主题方案,并理解仓库内配套脚本与示例代码是如何验证这些流程的。

技能定位:主题文档在 ui-styling 中的角色

ui-ux-pro-max-skill 是一个为 AI Agent 提供 UI/UX 设计智能的开源技能仓库。其中 ui-styling 技能 负责 shadcn/ui 组件、Tailwind CSS 工具类样式与 Canvas 视觉设计,references/shadcn-theming.md是它的主题与定制参考,覆盖:

  • next-themes 暗色模式接入;
  • CSS 变量系统;
  • 颜色定制与调色板;
  • 组件变体定制;
  • Theme Toggle 实现。

技能的主文档 SKILL.md 给出的前置流程是:

npx shadcn@latest init npx shadcn@latest add button card dialog form

init会提示选择框架、TypeScript、路径与主题偏好,并同时配置 shadcn/ui 和 Tailwind CSS。主题定制正是在init产出的components.jsonglobals.css之上进行的。另外,该文档在 CLI 资产树中存在镜像副本 cli/assets/skills/ui-styling/references/shadcn-theming.md,供 CLI 分发时引用。

暗色模式接入

shadcn/ui 的暗色模式采用class 策略:通过在<html>上切换darkclass,让 CSS 变量在:root.dark两套取值之间切换。这要求 Tailwind 配置中启用darkMode: ["class"](见下文"Tailwind 配置"一节)。

Next.js App Router

第 1 步:安装 next-themes

npm install next-themes

第 2 步:创建主题 Provider 包装组件

// components/theme-provider.tsx "use client" import * as React from "react" import { ThemeProvider as NextThemesProvider } from "next-themes" export function ThemeProvider({ children, ...props }: React.ComponentProps<typeof NextThemesProvider>) { return <NextThemesProvider {...props}>{children}</NextThemesProvider> }

第 3 步:在根布局中包裹应用

// app/layout.tsx import { ThemeProvider } from "@/components/theme-provider" export default function RootLayout({ children }) { return ( <html lang="en" suppressHydrationWarning> <body> <ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange > {children} </ThemeProvider> </body> </html> ) }

关键参数说明:

  • attribute="class":将主题写为<html>上的 class,与 Tailwind 的dark:变体联动;
  • defaultTheme="system"+enableSystem:无本地偏好时跟随系统prefers-color-scheme;
  • disableTransitionOnChange:切换主题时禁用过渡动画,避免颜色渐变闪烁;
  • <html>上的suppressHydrationWarning用于抑制 SSR 与客户端 hydration 之间的属性差异告警。

第 4 步:主题切换组件

import { Moon, Sun } from "lucide-react" import { useTheme } from "next-themes" import { Button } from "@/components/ui/button" export function ThemeToggle() { const { setTheme, theme } = useTheme() return ( <Button variant="ghost" size="icon" onClick={() => setTheme(theme === "light" ? "dark" : "light")} > <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> ) }

双图标通过rotate/scale过渡实现旋转淡入淡出,sr-only保证屏幕阅读器仍可感知"切换主题"。

仓库内的实际实现参考

仓库的 gallery 示例站 完整演示了这套方案。其 Provider 写法为:

"use client"; import { ThemeProvider } from "next-themes"; export function Providers({ children }: { children: React.ReactNode }) { return ( <ThemeProvider attribute="class" defaultTheme="system" enableSystem> {children} </ThemeProvider> ); }

gallery/package.json 中固定了"next-themes": "^0.4.4",可以作为选型参考。

切换按钮 gallery/components/DarkModeToggle.tsx 在文档版本基础上多做了一处关键处理——mounted 守卫,用于规避 next-themes 的服务端渲染陷阱(SSR 阶段theme恒为"system",直接渲染会与服务端输出不一致):

const { theme, setTheme } = useTheme(); const [mounted, setMounted] = useState(false); useEffect(() => setMounted(true), []); if (!mounted) { return <div className="w-9 h-9" />; // 占位,防止布局跳动 } const isDark = theme === "dark";

挂载前渲染固定尺寸的占位元素,挂载后再渲染真实按钮,并附带aria-label说明切换目标。这个模式值得在任何使用useTheme的组件中沿用。

Vite 或其他框架

非 Next.js 项目可以用 next-themes,也可以直接用原生方案:切换darkclass 并用localStorage持久化偏好,首次访问时回退到系统偏好:

// Store preference function toggleDarkMode() { const isDark = document.documentElement.classList.toggle('dark') localStorage.setItem('theme', isDark ? 'dark' : 'light') } // Initialize on load if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) { document.documentElement.classList.add('dark') }

该方案的核心逻辑:已存dark→ 启用;无存储记录且系统偏好深色 → 启用;否则保持浅色。初始化脚本应内联在<head>中执行,以避免暗色模式下的白屏闪烁(FOUC)。

CSS 变量系统

shadcn/ui 的主题令牌以 CSS 自定义属性形式定义在globals.css中,@layer base内同时声明浅色(:root)与深色(.dark)两套取值:

@layer base { :root { --background: 0 0% 100%; --foreground: 222.2 84% 4.9%; --primary: 222.2 47.4% 11.2%; --primary-foreground: 210 40% 98%; --secondary: 210 40% 96.1%; --secondary-foreground: 222.2 47.4% 11.2%; --muted: 210 40% 96.1%; --muted-foreground: 215.4 16.3% 46.9%; --accent: 210 40% 96.1%; --accent-foreground: 222.2 47.4% 11.2%; --destructive: 0 84.2% 60.2%; --destructive-foreground: 210 40% 98%; --border: 214.3 31.8% 91.4%; --input: 214.3 31.8% 91.4%; --ring: 222.2 84% 4.9%; --radius: 0.5rem; } .dark { --background: 222.2 84% 4.9%; --foreground: 210 40% 98%; --primary: 210 40% 98%; --primary-foreground: 222.2 47.4% 11.2%; --secondary: 217.2 32.6% 17.5%; --secondary-foreground: 210 40% 98%; --muted: 217.2 32.6% 17.5%; --muted-foreground: 215 20.2% 65.1%; --accent: 217.2 32.6% 17.5%; --accent-foreground: 210 40% 98%; --destructive: 0 62.8% 30.6%; --destructive-foreground: 210 40% 98%; --border: 217.2 32.6% 17.5%; --input: 217.2 32.6% 17.5%; --ring: 212.7 26.8% 83.9%; } }

这些变量遵循语义命名:background/foreground表达页面基底,primary/secondary/muted/accent表达角色而非具体色相,destructive表达"危险操作"含义,border/input/ring覆盖边框、输入框与焦点环。每个主色都配对一个-foreground变量,用于保证文字在该背景上的对比度。

颜色格式:裸 HSL 值

变量值采用不带hsl()包裹的 HSL 三分量格式,这是 shadcn/ui 的一个标志性设计:

--primary: 222.2 47.4% 11.2%; /* H S L */

使用时手动拼入hsl(),好处是可以直接用现代 CSS 的斜杠语法控制不透明度,而无需再引入--primary-alpha之类的第二组变量:

background: hsl(var(--primary)); background: hsl(var(--primary) / 0.5); /* 50% opacity */

如果写成--primary: hsl(222.2, 47.4%, 11.2%),则无法用hsl(var(--primary) / 0.5)这种形式调节透明度,这也是 shadcn/ui 组件库(如bg-primary/10这类 Tailwind 写法)能够成立的前提。

Tailwind 配置:把 CSS 变量映射为工具类

tailwind.config.ts负责把 CSS 变量桥接为 Tailwind 颜色与圆角工具类:

// tailwind.config.ts export default { darkMode: ["class"], theme: { extend: { colors: { border: "hsl(var(--border))", input: "hsl(var(--input))", ring: "hsl(var(--ring))", background: "hsl(var(--background))", foreground: "hsl(var(--foreground))", primary: { DEFAULT: "hsl(var(--primary))", foreground: "hsl(var(--primary-foreground))", }, secondary: { DEFAULT: "hsl(var(--secondary))", foreground: "hsl(var(--secondary-foreground))", }, destructive: { DEFAULT: "hsl(var(--destructive))", foreground: "hsl(var(--destructive-foreground))", }, muted: { DEFAULT: "hsl(var(--muted))", foreground: "hsl(var(--muted-foreground))", }, accent: { DEFAULT: "hsl(var(--accent))", foreground: "hsl(var(--accent-foreground))", }, }, borderRadius: { lg: "var(--radius)", md: "calc(var(--radius) - 2px)", sm: "calc(var(--radius) - 4px)", }, }, }, }

要点:

  • darkMode: ["class"]启用 class 策略,是整套暗色模式的前提;
  • 每个令牌色都映射为hsl(var(--*))形式,使bg-primarytext-muted-foregroundborder-input等工具类自动获得运行时主题能力;
  • 圆角三档由单一--radius派生:lg取原值,md减 2px,sm减 4px,形成层级一致的圆角体系。

仓库的 gallery/tailwind.config.ts 是最小化示例,其中darkMode: "class"与上表策略一致;gallery/app/globals.css 则演示了应用层如何配合暗色模式——在@layer base里给body设置@apply bg-gray-50 text-gray-900 dark:bg-gray-950 dark:text-gray-100,让全局基底色随darkclass 切换。

自动化辅助:tailwind_config_gen.py

ui-styling 技能附带脚本 tailwind_config_gen.py,可以程序化生成tailwind.config.ts/.js。它的基线配置(_base_config)默认写入"darkMode": ["class"],并按框架预设content路径:

  • react:./src/**/*.{js,jsx,ts,tsx}./index.html;
  • nextjs:./app/**./pages/**./components/**三类 glob;
  • vue/svelte亦有对应模板。

常用用法:

# 为 Next.js 生成 TypeScript 配置 python tailwind_config_gen.py --framework nextjs # 生成 JS 配置并注入自定义颜色(值可以是 hex 或 hsl(var(--primary)) 形式) python tailwind_config_gen.py --js --colors brand:#3b82f6 accent:#8b5cf6 # 追加自定义字体、间距、断点与推荐插件 python tailwind_config_gen.py --fonts display:"Playfair Display,serif" \ --spacing navbar:4rem --breakpoints 3xl:1920px --plugins

--plugins会追加推荐项(默认tailwindcss-animate,Next.js 下额外推荐@tailwindcss/typography)并打印对应的npm install -D命令。从源码看,_format_plugins对插件名执行严格的 npm 包名正则校验(拒绝引号、括号、分号等字符)后才生成require('...')语句,以防配置注入(源码注释明确引用了 CWE-94)。配套测试见 test_tailwind_config_gen.py。

颜色定制:三种方法

方法 1:直接修改 CSS 变量

最直接的改色方式是编辑globals.css中的变量值,同时维护浅色与深色两套:

:root { --primary: 262.1 83.3% 57.8%; /* Purple */ --primary-foreground: 210 20% 98%; } .dark { --primary: 263.4 70% 50.4%; /* Darker purple */ --primary-foreground: 210 20% 98%; }

注意深色下通常需要同步降低饱和度/明度,否则紫色在深色背景上会显得"发飘"。由于变量被 Tailwind 工具类全局引用,改一处即全站生效。

方法 2:主题生成器

可以使用 shadcn/ui 官方主题生成器(shadcn/ui 官方站点的 Themes 页面):选择基础色 → 生成主题 → 复制输出的一组 CSS 变量,整体替换globals.css中对应取值。这样生成的调色板在各语义角色间已做对比度配平,比手工逐变量试色更稳。

方法 3:多主题(data 属性)

在同一页面提供多套主题变体时,用data-theme属性承载额外变量组:

[data-theme="violet"] { --primary: 262.1 83.3% 57.8%; --primary-foreground: 210 20% 98%; } [data-theme="rose"] { --primary: 346.8 77.2% 49.8%; --primary-foreground: 355.7 100% 97.3%; }

然后在任意容器上应用,变量作用域自动收敛到该子树:

<div>// components/ui/button.tsx const buttonVariants = cva( "inline-flex items-center justify-center rounded-md text-sm font-medium", { variants: { variant: { default: "bg-primary text-primary-foreground", destructive: "bg-destructive text-destructive-foreground", outline: "border border-input bg-background", // Add custom variant gradient: "bg-gradient-to-r from-purple-500 to-pink-500 text-white", }, size: { default: "h-10 px-4 py-2", sm: "h-9 rounded-md px-3", lg: "h-11 rounded-md px-8", // Add custom size xl: "h-14 rounded-md px-10 text-lg", }, }, defaultVariants: { variant: "default", size: "default", }, } )

新增后可直接消费:

<Button variant="gradient" size="xl">Custom Button</Button>

定制基础样式

也可以直接改写组件的根类名,例如给 Card 加更强的阴影:

// components/ui/card.tsx const Card = React.forwardRef< HTMLDivElement, React.HTMLAttributes<HTMLDivElement> >(({ className, ...props }, ref) => ( <div ref={ref} className={cn( "rounded-xl border bg-card text-card-foreground shadow-lg", // Modified className )} {...props} /> ))

组件普遍把外部传入的className通过cn(tailwind-merge 封装)拼在默认类名之后,这为下一层覆盖留了口。

className 一次性覆盖

无需改源码的场景,直接传className即可:

<Card className="border-2 border-purple-500 shadow-2xl hover:scale-105 transition-transform"> Custom styled card </Card>

决策顺序建议:一次性需求 →className;多处复现 → 提取自定义变体;全局默认 → 改组件源码或 CSS 变量。

辅助脚本:shadcn_add.py

组件文件本身由 shadcn_add.py 这类工具管理。该脚本封装 shadcn CLI,支持--all--overwrite--dry-run--list--project-root:

python shadcn_add.py button card dialog # 添加多个组件 python shadcn_add.py --all # 添加全部组件 python shadcn_add.py button --overwrite # 强制重装 python shadcn_add.py --list # 列出已安装组件

从源码看,它有两处与主题流程直接相关的行为:一是先检查components.json是否存在,未init时直接报"shadcn not initialized. Run 'npx shadcn@latest init' first";二是_get_shadcn_version会从项目package.jsondependencies/devDependencies读取已锁定的 shadcn 版本来执行npx shadcn@<ver> add,读取不到时回退到内置的2.3.0,避免混装版本。已装组件的判定依据是components.jsonaliases.components指向目录下实际存在的.tsx文件。回归测试见 test_shadcn_add.py。

基础色预设、风格变体与全局圆角

基础色预设(Base Color Presets)

init时 shadcn/ui 提供五组灰阶基底:

  • Slate:偏冷的灰;
  • Gray:中性灰;
  • Zinc:偏暖的灰;
  • Neutral:均衡灰;
  • Stone:大地色灰。

初始化时选择,也可以事后通过更新 CSS 变量中的灰阶令牌(--background--muted--border等)更换。

组件风格变体(Style Variants)

两种组件风格:

  • Default:更柔和、更圆角的观感;
  • New York:更锐利、对比更强。

init交互中选择,或在components.json中声明:

{ "style": "new-york", "tailwind": { "cssVariables": true } }

tailwind.cssVariables: true即表示采用本文所述的 CSS 变量主题机制;切换 style 后重新add组件,会以对应风格的类名重写组件源码。

圆角全局控制

--radius一个变量控制全站圆角基调:

:root { --radius: 0.5rem; /* Default */ --radius: 0rem; /* Sharp corners */ --radius: 1rem; /* Rounded */ }

配合 Tailwind 配置中的派生映射,rounded-lg/rounded-md/rounded-sm全部跟随该变量缩放,实现"改一处、全局一致"的圆角体系。

最佳实践

原文档给出的 7 条主题工程准则,结合仓库实现可以逐条落实:

  1. 使用 CSS 变量:令牌化是运行时切换主题(暗色、多主题)的基础,这也是 shadcn/uicssVariables模式的核心;
  2. 前景色成对管理:每个语义色都配-foreground,避免只改背景导致文字对比度崩坏;
  3. 双主题验证:组件必须在 light 与 dark 两种模式下逐一检查,dark:变体应覆盖所有主题化元素;
  4. 语义化命名:用destructive而非redmuted而非gray,让样式意图可读、可替换;
  5. 可访问性:颜色对比度至少满足 WCAG AA(详见同目录参考文档 shadcn-accessibility.md);
  6. 组件覆盖优先 className:一次性差异用classNameprop,不污染组件默认值;
  7. 模式提取:反复出现的定制应沉淀为自定义 cva 变体或独立组件。

参考文件索引

用途路径
主题参考文档(本文主体).claude/skills/ui-styling/references/shadcn-theming.md
ui-styling 技能主文档.claude/skills/ui-styling/SKILL.md
组件安装脚本.claude/skills/ui-styling/scripts/shadcn_add.py
Tailwind 配置生成脚本.claude/skills/ui-styling/scripts/tailwind_config_gen.py
安装脚本测试.claude/skills/ui-styling/scripts/tests/test_shadcn_add.py
配置生成脚本测试.claude/skills/ui-styling/scripts/tests/test_tailwind_config_gen.py
暗色模式 Provider 示例gallery/app/providers.tsx
主题切换按钮示例gallery/components/DarkModeToggle.tsx
全局样式(暗色适配)gallery/app/globals.css
Tailwind 配置示例gallery/tailwind.config.ts
依赖版本(next-themes)gallery/package.json

【免费下载链接】ui-ux-pro-max-skillAn AI skill that provides design intelligence for building professional UI/UX across multiple platforms.项目地址: https://gitcode.com/gh_mirrors/ui/ui-ux-pro-max-skill

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询