shadcn/ui 安装与主题定制完全指南(含 CSS 变量)
说实话,第一次用 shadcn/ui 的时候,我被它”不是 npm 包”这个设定搞蒙了。复制代码到项目里?这听起来也太原始了吧?
用了几次之后才发现,这才是它厉害的地方——你拥有所有组件的源码,想怎么改就怎么改,不用担心版本冲突,也不用被组件库的设计限制死。
今天我们就聊聊 shadcn/ui 的安装配置和主题定制,重点是怎么用 CSS 变量实现品牌化的设计系统。读完这篇文章,你应该能在 5 分钟内搞定基础配置,再花个把小时把主题调整成你想要的样子。
一、快速安装:两种方式
方式一:CLI 一键初始化(推荐)
新项目直接用这个命令:
npx shadcn@latest init
运行后会问你一堆问题:用 TypeScript 还是 JavaScript?选哪种风格?默认主题是什么?全程交互式,跟着提示选就行。
安装完之后,你的项目里会多几个文件:
components.json- 配置文件lib/utils.ts- 工具函数components/ui/- 组件存放目录
想添加组件也简单,比如要个按钮:
npx shadcn@latest add button
组件代码会自动复制到 components/ui/button.tsx,你直接 import 用就行。
这里有个坑:如果你项目已经开发了一段时间,tailwind.config.js 和 globals.css 里可能已经写了不少东西。shadcn 的 init 命令会覆盖这些文件,所以最好在项目刚开始的时候就装。
有个博主说得挺对:把 shadcn/ui 当成项目的”第一批依赖”来装,别等到后面再加。血的教训。
方式二:手动安装(适合现有项目)
如果你的项目已经成型,CLI 覆盖配置风险太大,那就手动装。
分几步走:
第一步:确保装了 Tailwind CSS
shadcn 的组件都是用 Tailwind 写的,没装的话先装 Tailwind。这个不多说,官网教程很清楚。
第二步:安装依赖
npm install class-variance-authority clsx tailwind-merge
npm install lucide-react
class-variance-authority(简称 CVA)是个好东西,后面做组件变体时你会用到的。
第三步:配置路径别名
在 tsconfig.json 里加上:
{
"compilerOptions": {
"paths": {
"@/*": ["./*"]
}
}
}
这样你就可以用 @/components/ui/button 这种方式引入组件,不用写一堆 ../../../。
第四步:创建 components.json
在项目根目录新建这个文件:
{
"style": "new-york",
"rsc": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "app/globals.css",
"baseColor": "neutral",
"cssVariables": true
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}
cssVariables: true 这行很关键,表示我们要用 CSS 变量做主题,而不是 Tailwind 的 utility class。
第五步:添加样式
在 globals.css 里加上 shadcn 的基础样式,这个后面讲主题时会详细说。
二、理解主题系统:CSS 变量怎么玩
shadcn/ui 的主题系统基于一个简单的约定:每个颜色都有 background 和 foreground 两个变量。
啥意思?举个例子:
:root {
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
}
--primary 是按钮的背景色,--primary-foreground 是按钮上的文字颜色。这样配对的好处是,你只要改一个变量,相关的所有组件都会跟着变。
CSS 变量列表
shadcn/ui 默认定义了这些变量:
| 变量 | 用途 |
|---|---|
--background | 页面背景色 |
--foreground | 页面文字色 |
--card | 卡片背景 |
--card-foreground | 卡片文字 |
--popover | 弹出层背景 |
--popover-foreground | 弹出层文字 |
--primary | 主色(按钮、链接) |
--primary-foreground | 主色上的文字 |
--secondary | 次要颜色 |
--secondary-foreground | 次要颜色上的文字 |
--muted | 柔和背景 |
--muted-foreground | 柔和文字 |
--accent | 强调色 |
--accent-foreground | 强调色上的文字 |
--destructive | 危险操作(删除按钮) |
--destructive-foreground | 危险操作上的文字 |
--border | 边框 |
--input | 输入框 |
--ring | 焦点环 |
看着挺多,其实理解了 background/foreground 这个套路,就很好记。
HSL 格式的秘密
你可能注意到,shadcn 的颜色值不是标准的 HSL 格式:
/* ❌ 标准 HSL */
--primary: hsl(222.2, 47.4%, 11.2%);
/* ✅ shadcn 格式 */
--primary: 222.2 47.4% 11.2%;
为啥要写成这种”裸”的格式?
因为 Tailwind 支持透明度修饰符,比如 bg-primary/50 表示 50% 透明度的主色。如果你的变量是完整的 hsl() 格式,这个功能就用不了。
写成裸格式之后,Tailwind 会自动给你加上 hsl() 和透明度。聪明的设计。
三、定制你的品牌主题
方法一:直接改 CSS 变量
最简单的方式,打开 globals.css,找到 :root 部分,改颜色值就行。
比如你想把主色从默认的蓝色改成紫色:
:root {
--primary: 270 60% 60%;
--primary-foreground: 0 0% 100%;
}
.dark {
--primary: 270 60% 70%;
--primary-foreground: 0 0% 0%;
}
改完保存,所有用了 bg-primary 的按钮、链接都会变成紫色。
方法二:用 OKLCH 颜色空间(Tailwind v4)
如果你用的是 Tailwind v4,可以考虑用 OKLCH 颜色空间。相比 HSL,OKLCH 的颜色感知更接近人眼,生成的色阶更均匀。
:root {
--primary: oklch(0.6 0.2 270);
--primary-foreground: oklch(0.98 0 0);
}
这里 oklch(0.6 0.2 270) 的三个参数是:
0.6- 亮度(0-1)0.2- 彩度(0-0.4 左右)270- 色相角度(0-360)
方法三:在线工具生成
觉得自己配颜色太麻烦?可以用在线工具。
选个主色,工具会自动生成完整的 CSS 变量组,包括亮色和暗色两套。复制粘贴到 globals.css 就行。
四、暗色模式配置
用 next-themes 实现主题切换
shadcn/ui 本身不带主题切换功能,但可以用 next-themes 这个库来搞定。
先安装:
npm install next-themes
然后在 layout.tsx 里配置:
import { ThemeProvider } from "next-themes"
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="zh" suppressHydrationWarning>
<body>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
>
{children}
</ThemeProvider>
</body>
</html>
)
}
几个关键点:
suppressHydrationWarning必须加,不然会有水合警告attribute="class"表示用 class 名切换主题defaultTheme="system"表示默认跟随系统enableSystem开启系统主题检测
创建主题切换按钮
用 useTheme hook 获取当前主题和切换函数:
import { useTheme } from "next-themes"
import { Moon, Sun } from "lucide-react"
export function ThemeToggle() {
const { theme, setTheme } = useTheme()
return (
<button
onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
className="p-2 rounded-md hover:bg-accent"
>
{theme === "dark" ? <Sun size={20} /> : <Moon size={20} />}
</button>
)
}
默认暗色模式
如果你想让网站默认就是暗色模式,有两种方式:
方式一:硬编码 dark class
<html lang="zh" className="dark">
这样写的话,主题就固定成暗色了,没法切换。
方式二:设置默认主题
<ThemeProvider
attribute="class"
defaultTheme="dark" // 默认暗色
enableSystem={false} // 关闭系统检测
>
这样用户可以手动切换,但初始状态是暗色。
五、高级定制:组件变体
用 CVA 创建自定义变体
有时候你需要给按钮加几种样式,比如”危险”、“成功”、“渐变”。用 CVA 可以很方便地定义这些变体。
import { cva, type VariantProps } from "class-variance-authority"
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {}
然后在组件里用:
<button className={buttonVariants({ variant: "destructive", size: "lg" })}>
删除
</button>
不要直接改 shadcn 组件源码
这是个最佳实践问题。
shadcn 的组件代码在你自己的项目里,想怎么改都行。但建议不要直接改原始文件,而是创建包装组件。
为啥?因为 shadcn 经常更新组件,如果你改了原始文件,更新的时候就得手动 merge,很麻烦。
更好的方式:
// components/brand-button.tsx
import { Button } from "@/components/ui/button"
import { cva } from "class-variance-authority"
const brandButtonVariants = cva("...", {
variants: {
brand: {
primary: "bg-brand-primary text-white",
secondary: "bg-brand-secondary text-black",
},
},
})
export function BrandButton({ brand, ...props }) {
return <Button className={brandButtonVariants({ brand })} {...props} />
}
这样原始的 Button 组件保持不变,你创建了自己的 BrandButton,以后 shadcn 更新也不会影响你的定制。
六、常见问题和踩坑
问题一:安装后样式不生效
检查这几件事:
globals.css是否在layout.tsx里 import 了?- Tailwind 的
content配置是否包含了components/**/*? components.json的路径配置对不对?
问题二:主题切换时闪烁
这个通常是水合不匹配导致的。确保:
<html>标签加了suppressHydrationWarning- ThemeProvider 包裹了整个应用
- 不要在服务端渲染时读取 theme(会 undefined)
问题三:CSS 变量不生效
可能的原因:
- 变量名写错了(注意是
--primary-foreground不是--primaryForeground) - 没有对应的
.dark样式 - 变量值格式不对(要用裸 HSL 或 OKLCH)
问题四:组件样式冲突
如果你的项目已经有一套样式系统,和 shadcn 的可能有冲突。解决方案:
- 给 shadcn 组件加 namespace(比如
shadcn-button) - 调整 Tailwind 的 layer 优先级
- 用 CVA 创建自己的变体,不依赖默认样式
七、总结
shadcn/ui 的安装配置其实挺简单的,关键是理解它”复制代码而非依赖包”的设计理念。这样做的好处是完全可控,坏处是每个项目都得自己维护一套组件代码。
主题定制方面,CSS 变量系统设计得很优雅,你只要改几个变量值,整个应用的配色就会跟着变。配合 next-themes,亮色暗色切换也就几行代码的事。
最后几个建议:
- 新项目优先用 CLI 初始化,省去手动配置的麻烦
- 用语义化的颜色变量,比如 primary、secondary,而不是具体的颜色名
- 测试亮色和暗色模式下的对比度,确保可读性
- 创建包装组件而非修改源码,方便后续更新
下次你需要快速搭建一个带主题的 UI 时,试试 shadcn/ui。复制粘贴的快乐,谁用谁知道。
shadcn/ui 安装与主题定制
从零开始安装 shadcn/ui,配置主题系统,实现品牌化设计
⏱️ 预计耗时: 30 分钟
- 1
步骤1: CLI 快速初始化
在新项目中运行安装命令:
• npx shadcn@latest init
• 选择 TypeScript/New York 风格/默认主题
• 等待 CLI 完成配置 - 2
步骤2: 修改品牌主色
编辑 globals.css 中的 CSS 变量:
• 打开 app/globals.css
• 找到 :root 下的 --primary 变量
• 修改为你的品牌色(HSL 或 OKLCH 格式)
• 同时修改 --primary-foreground 确保对比度 - 3
步骤3: 配置暗色模式
安装并配置 next-themes:
• npm install next-themes
• 在 layout.tsx 中添加 ThemeProvider
• 设置 suppressHydrationWarning 避免水合警告
• 创建主题切换组件 - 4
步骤4: 创建组件变体
使用 CVA 定义自定义样式:
• 安装 class-variance-authority
• 定义 variants 和 defaultVariants
• 在组件中应用 buttonVariants()
• 保持原始 shadcn 组件不变
常见问题
shadcn/ui 和传统 UI 组件库有什么区别?
为什么建议在新项目初始化时就安装 shadcn/ui?
CSS 变量为什么要写成裸格式而不是标准 HSL?
如何修改品牌主色?
暗色模式为什么会出现闪烁?
应该直接修改 shadcn 组件源码吗?
参考资料
- shadcn/ui 官方文档 - Installation
- shadcn/ui 官方文档 - Theming
- shadcn/ui 官方文档 - Dark Mode
- Generate Custom shadcn/ui Themes
- Theming in shadcn UI: CSS Variables
9 分钟阅读 · 发布于: 2026年3月26日 · 修改于: 2026年3月26日
相关文章
shadcn/ui 是什么?与 MUI、Chakra 组件库对比选型指南
shadcn/ui 是什么?与 MUI、Chakra 组件库对比选型指南
Tailwind CSS v4 新特性解读:性能、配置与迁移指南
Tailwind CSS v4 新特性解读:性能、配置与迁移指南
Tailwind v4 + Vite:5 分钟完整配置模板与目录结构

评论
使用 GitHub 账号登录后即可评论