shadcn/ui 与 HeroUI v3 的 Design Tokens:覆盖范围、项目够用程度与网站设计边界
shadcn/ui Theming · HeroUI Theming
HeroUI Colors:用户提供的入口 · Default Theme Variables · Shared Theme Utilities
AI 参与说明(Agent:Codex):本文通过 Context7、官方文档和固定提交的公开 CSS 源码整理,比较 shadcn/ui 与 HeroUI v3 的主题变量、基础尺度和组件状态,并给出网站设计的实践建议。资料核验日期为 2026-10-22;项目是否够用及选型建议属于基于这些证据的工程判断。执行入口 Codex Desktop;本轮模型完整标识与 reasoning effort 未取得任务级运行记录。中文正文按项目 humanizer-zh 编辑。
如果比较默认提供的通用语义角色、表单细分、反馈状态和全局视觉参数,HeroUI v3 覆盖更广。shadcn/ui 的核心主题更精简,但自带图表和侧栏专用角色,还能结合 Tailwind 与可选的 Typeset 完成常见网站。一般项目用任意一套作为基础通常都够,真正需要补的是项目自己的布局、排版、业务语义和使用规则。
这里的“够用”指能够用少量一致的扩展完成网站,不表示只改几个变量就完成网站设计,也不表示组件库替项目完成了无障碍验收。
比较范围与术语
本文比较 HeroUI v3 开源默认样式与当前 shadcn/ui Theming 约定。不把 HeroUI v2 的主题插件、HeroUI Native 或 HeroUI Pro 的额外 token 混入开源默认体系。shadcn/ui 则区分核心主题、Tailwind 基础尺度及可选的 Typeset。
| 英文术语 | 中文名称 | 简要解释 |
|---|---|---|
| Design Token | 设计令牌 | 为可复用设计值命名,例如主行动颜色、圆角或阴影 |
| Primitive Token | 基础令牌 | 描述基础值,例如某个蓝色、间距尺度或字体;本文采用的分析分层 |
| Semantic Token | 语义令牌 | 按用途命名,例如危险操作颜色;同样的角色可在深浅色下取不同值 |
| Component Token | 组件令牌 | 面向具体组件或部件的值,例如按钮按下背景;局部实现变量未必是稳定的公开接口 |
| Recipe | 样式配方 | 将尺寸、颜色、状态选择器等组合成组件样式的规则 |
| Utility | 工具类 | 对应某项样式的 CSS 类,例如 bg-accent 或 gap-4 |
| Typeset | 排版体系 | 此处特指 shadcn/typeset,为 HTML 与 Markdown 内容组织字体和阅读节奏 |
The number of CSS variables is not a measure of design-system completeness. Semantic coverage and actual component consumption matter more than raw counts.
CSS 自定义属性是实现 Design Token 的一种方式。一个属性也可能只是别名、中间计算值或组件内部变量;反过来,某种视觉状态可以由 Recipe 计算,并不一定拥有独立变量。DTCG Format 2025.10描述了 token 的值、类型和引用方式,但不能据此认定这两个库已经实现完整的 DTCG 交换格式。该文档属于 Community Group Report,并非 W3C Standard。
本次源码快照:
| 对象 | 核验范围 |
|---|---|
| shadcn/ui | 2026-10-22 的 Theming、Typeset 文档;公开源码提交 ba1503c64472b82256d9de95e2e62c5901a38e8b |
| HeroUI | @heroui/styles 3.2.6 对应的 v3 分支快照 d814c204a92464ca02eb229c384a4a982d1ab398;默认变量、共享映射及相关组件 CSS |
shadcn/ui 组件由项目持有,已有项目未必已经采用当前文档中的全部变量和 Recipe;比较结果不能直接当作升级后会自动发生的变化。
先把三层分开
讨论覆盖范围时,需要分别检查:
- 基础尺度:颜色原值、字号、间距、尺寸、阴影、断点等。两者都大量承接 Tailwind,不能把这一层只算给某一方。
- 语义角色:品牌强调、弱化文字、浮层、表单、成功或危险等。这是默认 token 体系最值得直接比较的部分。
- 组件 Recipe:按钮如何变大、输入框如何显示错误、按下时是否缩放。很多细节写在组件样式中,未提升为全局 token。
例如 gap-4 表示从间距尺度取值;“表单行间距统一用多少”是项目规则;“按钮内部的图标间距取哪一档”属于组件 Recipe。这三件事相关,但不能互相替代。
覆盖范围对照
下表中的“默认”专指上述核验范围,不限制项目自行扩展。
| 维度 | shadcn/ui | HeroUI v3 开源默认体系 | 判断 |
|---|---|---|---|
| 页面基础色 | background、foreground | 相同角色,另有背景 secondary、tertiary、inverse 派生值 | HeroUI 更细 |
| 内容表面 | card / card-foreground,另有 muted 等辅助表面 | surface、surface-secondary、surface-tertiary,各有 foreground | HeroUI 的通用层级更明确 |
| 浮层 | popover / popover-foreground;遮罩在组件 Recipe 中定义 | overlay / overlay-foreground、backdrop | HeroUI 提供更多全局角色 |
| 主强调与辅助填充 | primary、secondary、accent 等配对角色 | accent、default 及各自 foreground、hover、soft 系列 | 命名模型不同,不能只比较数量 |
| 成功、警告、危险 | 核心默认有 destructive;success、warning、info 需扩展或来自其他组件方案 | success、warning、danger 及 foreground、hover、soft 系列;默认也没有独立 info | HeroUI 更全 |
| 弱化文字、链接、焦点 | muted-foreground、ring;链接常复用现有角色 | muted、link、focus | HeroUI 更显式 |
| 表单颜色 | input、border、ring,配合 Recipe 处理背景、占位和 invalid | 独立 field 背景、文字、占位、边框、hover、focus 及边框状态 | HeroUI 明显更细 |
| 边框与分隔线 | border,另有 sidebar 专用 border | border 与 separator 分开,均有 secondary、tertiary | HeroUI 更细 |
| 图表 | chart-1 至 chart-5,可继续扩展 ChartConfig | 开源默认主题文件没有对应 chart 系列 | shadcn 默认更直接 |
| 侧栏 | 独立 sidebar 背景、文字、强调、选中、边框、焦点,共 8 个颜色变量 | 开源默认没有 sidebar 专用语义分组 | shadcn 默认更直接 |
| 圆角 | 根 radius 派生 sm 至 4xl | 根 radius 派生 xs 至 4xl,另有 field radius | 都能统一;HeroUI 多一组表单控制 |
| 阴影 | 使用 Tailwind 阴影尺度与组件 Recipe | 额外提供 surface、overlay、field 角色阴影,深浅色可不同 | HeroUI 语义控制更方便 |
| 间距与尺寸 | Tailwind 尺度,组件选择 padding、gap、height | spacing 根值与 Tailwind 尺度,组件也有尺寸 Recipe | 都具备基础能力;都不替网站决定布局 |
| 字体与正文排版 | Tailwind;另有可选 Typeset 的 body、heading、mono 与阅读节奏 | 这两个主题文件没有独立 font、text 角色体系,主要使用 Tailwind 与组件 Recipe | 不能宣称 HeroUI 排版全面领先 |
| 动效 | 组件 transition、duration、animation 与样式工具 | 额外提供多组 easing、若干 animation、Skeleton 模式和 Tooltip 延迟 | HeroUI 暴露更多全局参数;都需项目编排 |
| 禁用与光标 | 多在 Recipe 中使用 opacity、pointer 等规则 | 有 disabled opacity、交互与禁用 cursor 变量 | HeroUI 更集中 |
| 滚动条与浮层层级 | 主要由具体组件或项目规则决定 | 有 scrollbar 系列;base.css 另有 overlay、toast z-index | HeroUI 提供更多控制点,仍需网站层级规则 |
| 深浅色与扩展 | 同名变量在 .dark 覆写;可增加变量和 Utility | class / data-theme 切换;CSS 变量与 BEM 覆盖;可自定义主题 | 两者都能扩展,不存在默认清单形成的硬上限 |
证据入口:shadcn Theming、Typeset、Nova 组件 Recipe、HeroUI 默认变量、共享映射、base.css。
为什么不直接比较“总共有多少 token”
按当前 shadcn Theming 页的默认清单去重,得到 31 个颜色变量:18 个通用 UI、5 个图表、8 个侧栏;另有一个根圆角变量。深色重复声明和 Tailwind 映射不重复计算。
HeroUI 的 variables.css 有 89 个不同的自定义属性声明名;shared/theme.css 有 101 个,其中 64 个是颜色映射,还有阴影、圆角、easing 等。不能将两者相加,称为 190 个独立设计决策:--color-accent 很大程度上是 --accent 接入 Tailwind 的另一个入口,很多状态又由基础值计算。
这些数字只能帮助核对文件范围。HeroUI 更广的依据是它确实给表单、状态、阴影等增加了可分别控制的角色;shadcn 的 5 个图表色也不能据此当成完整的数据可视化规范。
名称相同,语义可能不同
跨库迁移时,最容易出错的是逐字替换。
| shadcn/ui 角色 | HeroUI 中较接近的角色 | 注意事项 |
|---|---|---|
primary / primary-foreground | accent / accent-foreground | 都适合主行动与主强调;需要检查实际使用位置 |
accent / accent-foreground | 根据场景使用 default、surface-hover、选中样式等 | shadcn accent 常用于菜单高亮、ghost hover 等,HeroUI accent 是品牌主强调,不能同名直译 |
muted-foreground | muted | 两者都可用于弱化文字 |
muted | 按场景选择 surface 或 background 的某一层 | shadcn muted 常是弱化背景,HeroUI muted 是文字色;直接把它当背景会改变层次 |
card | surface | 可作为起点;surface 的多个层级需要重新分配 |
popover | overlay | 配对 foreground、shadow 和遮罩一起检查 |
destructive | danger | HeroUI 另有 soft、hover 等角色 |
ring | focus | 焦点颜色之外,还要检查线宽、offset 和组件状态 |
input | 主要检查 field-border 等表单角色 | 不能直接映射成 field-background;输入边框与背景是不同设计决策 |
映射表是迁移起点,不是两套体系的无损一一对应。依据分别见 shadcn Token Convention 与 HeroUI Colors。
token 怎样真正影响组件
HeroUI:基础语义、派生状态、Utility 与组件局部值
HeroUI 默认变量先定义品牌基础色及配对 foreground,再以 color-mix() 派生 hover 和 soft;共享文件将它们映射为 Tailwind 颜色入口;组件 Recipe 选择需要的角色。
例如主行动按钮可以沿以下关系追踪:
| 层次 | 输入与转换 | 结果 |
|---|---|---|
| 默认变量 | --accent 与 --accent-foreground | 定义品牌背景与其上的内容色 |
| 派生变量 | --accent-hover 混合 accent 与 accent foreground | 形成 hover 色,独立变量也允许继续覆盖 |
| 共享映射 | --color-accent-hover: var(--accent-hover) | 可通过 hover:bg-accent-hover 使用 |
| Button Recipe | primary 变体将局部背景变量接到 accent 系列 | Button 获得默认、hover 等外观 |
| 按下规则 | Button 的局部 pressed 背景默认复用 hover,并按尺寸缩放 | 按下反馈由颜色与形变共同表达 |
上游实现见 variables.css、theme.css 与 button.css。
因此,改 accent 会影响相关派生颜色与消费这些颜色的组件,但不会自动改变所有尺寸、布局或交互逻辑。当前默认体系也没有统一的全局 *-pressed 颜色家族;不能把 Button 内的局部变量泛化成所有组件都共享的契约。
shadcn:少量语义值也能表达多种状态
shadcn 的 Recipe 经常直接组合基础语义值与透明度。例如 Nova 的 destructive Badge 使用同一 destructive 色构造轻色背景、hover 背景及 focus ring。这说明缺少独立 destructive-hover 不等于没有 hover;它的变化规则保存在 Recipe 中。Nova 样式源码
取舍在修改成本:只换危险色,两种方式都方便;如果要把所有危险状态的 hover 规则从透明度变化改成特定混色,HeroUI 已有集中控制点,shadcn 项目可能需要修改相关 Recipe 或补充自己的 Semantic Token。源码由项目持有也给了 shadcn 很大的调整自由,但团队要负责约束和维护这些修改。
自动派生颜色不等于自动通过对比度验收。更换 accent、foreground、透明度或承载背景后,仍要检查真实组合;尤其 soft 背景会受其下方表面影响。无障碍要求还包括键盘、焦点、标签与状态信息,不能只看颜色。WCAG 2.2
排版、间距和动效到底算不算“有”
两者都有能力完成排版、留白、阴影与动效,区别是这些决策放在哪里。
shadcn 的核心主题清单没有把所有字号、间距、时长抽成网站级语义角色,但 Tailwind 尺度与组件 Recipe 已经在使用这些值。当前 shadcn/typeset 还提供独立的内容排版体系:body、heading、mono 字体,以及 size、leading、flow 三个主要阅读节奏控制点。它适合文档、文章、聊天内容,不能把 shadcn 概括成“没有排版体系”。
HeroUI 这两个主题文件也没有独立的 --font-*、--text-* 全套正文角色。它的主题页虽然把 typography 列入广义基础变量,但核对具体实现后,应将这里的能力归于 Tailwind 与组件 Recipe;不要据此推导出一个已经配置好的 display、heading、body、caption 产品排版规范。
间距同理。HeroUI 有根 --spacing,两者都用 Tailwind 尺度。统一修改基础单位可能影响很多尺寸,却不能保证内容密度合适。紧凑数据表、普通设置表单与大幅品牌首页往往需要不同的组合规则。
HeroUI 暴露了更多 easing、动画和延迟参数,但“展开菜单用哪条曲线”和“首页如何通过动效叙事”仍是不同层次的问题。全站动效还需要明确触发条件、节奏、可中断行为和减少动画时的替代。
一般项目够不够用
以常规组件和深浅色主题为起点,两者通常都够。 这是一项工程判断,前提是允许团队添加少量项目角色与页面规则,而不是要求每项设计决定都必须在库的默认清单中找到同名 token。
| 项目类型 | 是否通常够用 | 优先补什么 |
|---|---|---|
| 博客、文档站、知识库 | 两者都够作为基础 | 中文字体、正文宽度、标题节奏、代码与表格、目录和搜索路径;shadcn 可选 Typeset |
| 企业网站、常规产品官网 | 两者都够作为基础 | 栅格、section 节奏、首屏重点、图片和文案;组件主题只覆盖其中一部分 |
| 常规 SaaS、设置中心、业务表单 | 两者都够;HeroUI 默认状态与表单角色更省事 | 业务状态、复杂验证、空状态、错误恢复、表格密度与一致的页面模板 |
| 数据分析后台 | 都可作为基础,但需要补充 | 图表系列、顺序与发散色阶、数据语义、密集阅读规则;shadcn 自带五色只是起点 |
| 多品牌、多租户或密度切换平台 | 需要应用层主题与角色映射 | brand、mode、density 的组合规则和组件消费约束,不能只替换主色 |
| 作品集、活动页、视觉叙事网站 | token 足够表达很多值,成品仍高度依赖设计 | 艺术方向、构图、图像、字体、空间和动效编排 |
如果已经采用 HeroUI,并且喜欢其组件视觉,不值得仅因 shadcn 有 chart 或 sidebar 命名就迁移。给项目补这两类角色通常更直接。反过来,已有 shadcn 项目只缺 success、warning,也通常可以扩展现有主题,不必更换组件库。
当几乎每个组件都需要覆盖圆角、间距、状态和阴影,才应重新评估默认 Recipe 是否符合项目。默认 token 少,不自动意味着工作多;默认变量多,也不能弥补视觉方向不匹配。
用三个真实页面验证“够用”
选一页信息展示、一页复杂表单、一页列表或数据页,使用真实中文、长标题、长数字与真实状态:
- 定义页面所需的角色,记录默认已有、可复用和需要新增的项目项。
- 同时检查深浅色;逐项看文字层级、表面、边框、焦点、hover、invalid、disabled、pending、成功与失败反馈。
- 把重复覆盖写进项目 Recipe 或 Semantic Token。只出现一次的装饰不必立即提升为全局契约。
- 改一次主强调色与基础圆角,确认预期组件响应;另改一次表单外观或图表色,确认没有误伤无关角色。
- 检查完整任务路径:键盘可操作,错误可理解,浮层不遮挡关键操作,长内容仍可读。
通过标准是关键页面能复用同一组规则、缺口可解释且集中、主题修改能传播到预期位置。不要凭变量数量估算“覆盖 90%”;没有项目需求清单就不存在可核查的覆盖率。
一个最小扩展例子:库负责组件颜色,项目负责页面规则
下面可加入一个已经配置 HeroUI v3 与 Tailwind v4 的 React 项目。它演示主题 token 也能服务自建 HTML,以及项目如何补充内容宽度和 section 间距;不是完整的项目脚手架。
app.css:
@import "tailwindcss";
@import "@heroui/styles";
:root {
--accent: oklch(0.55 0.18 260);
--accent-foreground: oklch(0.99 0 0);
--radius: 0.625rem;
/* 项目自定义的页面角色,不属于 HeroUI 默认清单。 */
--site-content-max: 72rem;
--site-reading-max: 68ch;
--site-section-gap: clamp(2rem, 5vw, 5rem);
}
.dark,
[data-theme="dark"] {
--accent: oklch(0.76 0.12 260);
--accent-foreground: oklch(0.18 0.02 260);
}
.site-shell {
width: min(calc(100% - 2rem), var(--site-content-max));
margin-inline: auto;
padding-block: var(--site-section-gap);
}
.reading-copy {
max-width: var(--site-reading-max);
}
App.tsx:
import "./app.css";
export default function App() {
return (
<main className="min-h-screen bg-background text-foreground">
<section className="site-shell">
<div className="rounded-xl bg-surface p-6 shadow-surface">
<h1 className="text-2xl font-semibold">项目概览</h1>
<p className="reading-copy mt-3 text-muted">
组件颜色来自 HeroUI,正文宽度和页面节奏由项目定义。
</p>
<a
href="#details"
className="mt-5 inline-flex rounded-lg bg-accent px-4 py-2
text-accent-foreground hover:bg-accent-hover
focus-visible:outline-2 focus-visible:outline-offset-2
focus-visible:outline-focus"
>
阅读详情
</a>
</div>
<section id="details" className="reading-copy mt-8">
<h2 className="text-xl font-semibold">设计范围</h2>
<p className="mt-3">内容层级、布局和交互规则仍需项目决定。</p>
</section>
</section>
</main>
);
}
在文档核验时,上述 CSS 使用 @heroui/styles 3.2.6、Tailwind 4.3.3 编译,关键 Utility 与项目规则均生成;React 示例可渲染为 HTML。将根元素切到 class="dark" 可使用深色分支。这个例子检验主题接入与样式生成,实际产品仍应按自己的文字尺寸和承载背景检查对比度。
使用 shadcn 时也可以沿用这些项目页面角色;主行动换用 primary、正文弱化色换用 muted-foreground,卡片换用 card,再按项目 Recipe 处理阴影与状态。不要将两个库的整套全局 CSS 叠在一起解决映射问题:共享变量和 Utility 名称可能冲突,若确需共存,应明确主题归属、作用域和组件适配边界。
除了 token,一个网站还要设计什么
很多设计值都能进一步 token 化;但值的选择、组合、适用条件和用户任务仍需要设计。下面这些工作对成品的影响,往往比继续增加颜色变量更直接。
| 设计工作 | 需要做出的决定 | 可检查的产物 |
|---|---|---|
| 信息架构 | 用户来完成什么,栏目如何组织,怎样到达目标 | 导航树、页面层级、搜索与任务路径 |
| 页面布局 | 栅格、内容宽度、留白、首屏重点、阅读顺序 | 首页、列表、详情、表单等真实页面模板 |
| 排版 | 中英文字体、标题层级、正文行长、行高、长内容规则 | 用中文长文、数字、代码和表格验证的排版样本 |
| 组件组合 | 何时用主按钮、次按钮、Card、Dialog,哪些模式跨页面复用 | 组件示例、variants、禁止或不建议的用法 |
| 任务与交互 | 表单验证时机、筛选与分页、保存反馈、危险操作、撤销与恢复 | 完整交互流程及空、加载、失败、成功状态 |
| 品牌与艺术方向 | 图像、插画、图标、字体风格、材质和整体气质 | 视觉参考、品牌资产及真实页面构图 |
| 内容设计 | 标题、按钮措辞、错误说明、帮助文字、日期与数字格式 | 文案样本与内容规则 |
| 动效编排 | 哪些变化值得动画,注意力如何移动,如何中断和减弱 | 转场规则、状态过渡与减少动画版本 |
| 无障碍与适配 | 键盘路径、焦点、语义、对比度、缩放、重排与触控 | 真实页面和关键流程的验收结果 |
| 维护与治理 | token 与 Recipe 的命名、责任、变更和兼容 | 设计说明、组件目录、变更记录与必要的视觉回归 |
例如“正文最大宽度为 68ch”可以写成 token;但先展示结论还是先展示背景、侧栏放哪些内容、错误后如何继续,都不能靠这个数值决定。类似地,Button 的颜色和半径可以统一,但一个页面是否放了过多主按钮,仍是信息层级与交互选择问题。
一般网站建议的推进顺序
先确定用户任务与内容,再做两三张有代表性的页面,选定字体、主强调、表面层级、圆角和密度。随后统一反复出现的组件 Recipe,最后把稳定的重复决策提炼为项目 token。用真实深浅色、长内容和关键状态复验,再维护简短的使用规范。
采用 HeroUI 时,优先发挥已有的表单、状态和表面角色,补页面布局、排版与业务语义;采用 shadcn 时,复用核心主题、Tailwind 和需要的 Typeset,为重复缺失的状态补少量角色。两条路径都可以得到完整网站,决定结果的是规则能否在真实页面中成立、组件是否按规则消费,以及团队能否持续维护。
关联阅读
- Frontend 主题导航
- Base UI 与 React Aria:底层机制、现代 API 与 HeroUI 样式复用:进一步区分外观、组件组合和底层交互职责。