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 shadcnlatest init npx shadcnlatest add button card dialog forminit会提示选择框架、TypeScript、路径与主题偏好,并同时配置 shadcn/ui 和 Tailwind CSS。主题定制正是在init产出的components.json与globals.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-themesnpm 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.ComponentPropstypeof NextThemesProvider) { return NextThemesProvider {...props}{children}/NextThemesProvider }第 3 步:在根布局中包裹应用// app/layout.tsx import { ThemeProvider } from /components/theme-provider export default function RootLayout({ children }) { return ( html langen suppressHydrationWarning body ThemeProvider attributeclass defaultThemesystem enableSystem disableTransitionOnChange {children} /ThemeProvider /body /html ) }关键参数说明:attributeclass:将主题写为html上的 class,与 Tailwind 的dark:变体联动;defaultThemesystemenableSystem:无本地偏好时跟随系统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 variantghost sizeicon onClick{() setTheme(theme light ? dark : light)} Sun classNameh-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0 / Moon classNameabsolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100 / span classNamesr-onlyToggle 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 attributeclass defaultThemesystem 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 classNamew-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-primary、text-muted-foreground、border-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.pyui-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-themeviolet] { --primary: 262.1 83.3% 57.8%; --primary-foreground: 210 20% 98%; } [data-themerose] { --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 variantgradient sizexlCustom Button/Button定制基础样式也可以直接改写组件的根类名,例如给 Card 加更强的阴影:// components/ui/card.tsx const Card React.forwardRef HTMLDivElement, React.HTMLAttributesHTMLDivElement (({ 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 classNameborder-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 shadcnlatest init first;二是_get_shadcn_version会从项目package.json的dependencies/devDependencies读取已锁定的 shadcn 版本来执行npx shadcnver add,读取不到时回退到内置的2.3.0,避免混装版本。已装组件的判定依据是components.json中aliases.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 条主题工程准则,结合仓库实现可以逐条落实:使用 CSS 变量:令牌化是运行时切换主题(暗色、多主题)的基础,这也是 shadcn/uicssVariables模式的核心;前景色成对管理:每个语义色都配-foreground,避免只改背景导致文字对比度崩坏;双主题验证:组件必须在 light 与 dark 两种模式下逐一检查,dark:变体应覆盖所有主题化元素;语义化命名:用destructive而非red、muted而非gray,让样式意图可读、可替换;可访问性:颜色对比度至少满足 WCAG AA(详见同目录参考文档 shadcn-accessibility.md);组件覆盖优先 className:一次性差异用classNameprop,不污染组件默认值;模式提取:反复出现的定制应沉淀为自定义 cva 变体或独立组件。参考文件索引用途路径主题参考文档(本文主体).claude/skills/ui-styling/references/shadcn-theming.mdui-styling 技能主文档.claude/skills/ui-styling/SKILL.md组件安装脚本.claude/skills/ui-styling/scripts/shadcn_add.pyTailwind 配置生成脚本.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.cssTailwind 配置示例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),仅供参考