跳至正文
React — shadcn/ui 与 HeroUI v3 的 Design Tokens 详细对比:语义、状态、尺度与主题覆盖

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 编辑。

修订说明

关联阅读路径(2026-10-28,Agent:Cursor):网站 UI 设计与实现两篇的 relref 改为 docs/Frontend/UI/ 下的新路径。模型 grok-4.7-high-fast,提供方 xAI,执行入口 Cursor Cloud。reasoning effort 未作为独立字段取得运行记录。

关联阅读整理(2026-10-23,Agent:Codex):设计资料与方法统一指向网站 UI 设计主文,代码机制与实验指向实现专题;本篇保留原有比较范围与代码。执行入口为 Codex Desktop;本次模型完整标识与 reasoning effort 未取得任务级运行记录。

实践展开(2026-10-22,Agent:Codex):根据公开设计资料补齐信息架构、排版、视觉、布局、图像、交互、内容与设计系统方法,逐项说明案例与验收条件。新增方法属于来源核验后的原创综合,未读取付费正文;原 token 对比版本与已验证示例保持原范围。执行入口 Codex Desktop;模型完整标识与 reasoning effort 未取得任务级运行记录。

Token 对比展开(2026-10-22,Agent:Codex):再次通过 ctx7 查阅两家的主题文档,并对照下列固定版本源码,补齐角色清单、颜色派生、Button 与 Input 的状态、圆角公式、主题作用域及双份扩展示例;保留原有参考资料与网站设计延伸。执行入口 Codex Desktop;模型完整标识与 reasoning effort 未取得任务级运行记录。

全量对照表修订(2026-10-22,Agent:Codex):将覆盖范围表改为逐项清单,分别列全两家默认全局主题、Tailwind 桥接映射与 Tailwind 4.3.3 的 419 个原生主题声明名,并用源码声明集合核对遗漏;同日继续澄清模板声明、派生入口与组件 Recipe 的统计层次,不再把 32 项统称为基础核心 token。后文保留公式、组件消费与参考资料。执行入口 Codex Desktop;模型完整标识与 reasoning effort 未取得任务级运行记录。

结构修订(2026-10-22,Agent:Codex):按比较维度重新组织正文,保留完整 token 主表,将 Tailwind 索引后置为附录;通用网站设计方法独立成文并保留来源,补齐原锚点兼容。明确模板声明、派生入口与组件 Recipe 的层次。执行入口 Codex Desktop;模型完整标识与 reasoning effort 未取得任务级运行记录。

Class 分层补充(2026-10-23,Agent:Codex):通过 ctx7 核对 Tailwind Theme Variables、Utility 与 Variant,区分主题值、桥接名称、工具类、组件 Recipe 类和 React API;按固定源码补齐两家 Button 的实际消费路径。执行入口 Codex Desktop;模型完整标识与 reasoning effort 未取得任务级运行记录。

两家都能用 Tailwind 和组件样式完成常见界面;最值得比较的是设计决定放在哪里,以及修改时能否通过一个主题入口统一传播。 HeroUI v3 将更多表单与状态暴露为全局变量;shadcn/ui 的主题模板集中定义语义颜色和根圆角,许多状态、尺寸与风格选择保存在组件 Recipe 中。

本文依次比较语义与配色、状态派生、视觉尺度、专用角色、组件消费和主题修改。各节先对齐同一用途,再解释默认实现及改动成本;完整变量清单用于检索,不用总数推算设计能力。

阅读入口:变量与 class 分层 · 完整主表 · 组件状态对照 · 主题修改与示例 · Tailwind 完整索引。

比较范围与术语

本文固定比较 shadcn/ui 的 Theming 模板与 Nova Recipe、HeroUI v3.2.6 的开源默认样式,以及两者使用的 Tailwind CSS 4.3.3 基础主题。HeroUI v2、Native、Pro 和项目自行扩展的变量不计入默认清单。官方其他主题的色值可以不同,不能把某套配色推广为所有主题的默认值。

shadcn 部分采用官方推荐的 CSS variables 模式;它也允许生成直接使用 Tailwind 调色板类的组件。后者不以本文这组语义变量实现主题切换,应另查项目实际生成的 Recipe。shadcn:Without CSS Variables

英文术语中文名称简要解释
Design Token设计令牌为可复用设计值命名,例如主行动颜色或圆角
Primitive Token基础令牌描述基础值,例如某档蓝色;本文采用的分析分层
Semantic Token语义令牌按用途命名,例如危险操作颜色
Derived Token派生令牌由其他值计算,例如从主色与内容色混合得到 hover 色
Component Token组件令牌某个组件或部件使用的值;局部实现变量未必是稳定公开接口
Recipe样式配方将尺寸、变体和状态选择器组合成组件样式的规则
Theme Variable主题变量Tailwind 在 @theme 中注册的设计值或引用,命名空间决定可用的 Utility
Utility工具类对应具体样式的类,例如 bg-primary 或 gap-4;既可由 Tailwind 提供,也可由库或项目扩展
Variant条件或变体Tailwind 用它限定样式生效条件;组件 API 也用同名概念选择 Recipe,两者需要分别识别
Typeset排版体系此处特指可选的 shadcn/typeset,用于组织正文排版与阅读节奏

本次源码快照:

对象核验范围
shadcn/ui2026-10-22 的 Theming、Typeset 文档;公开源码提交 ba1503c64472b82256d9de95e2e62c5901a38e8b
HeroUI@heroui/styles 3.2.6 对应的 v3 分支快照 d814c204a92464ca02eb229c384a4a982d1ab398;默认变量、共享映射及相关组件 CSS
Tailwind CSStailwindcss 4.3.3 的默认 theme.css;当前项与 Deprecated 项分别标记

shadcn/ui 组件由项目持有,已有项目未必已经采用当前文档中的全部变量和 Recipe;比较结果不能直接当作升级后会自动发生的变化。

CSS 自定义属性是实现 Design Token 的一种方式。一个属性也可能只是别名、中间计算值或组件内部变量;反过来,某种视觉状态可以由 Recipe 计算,并不一定拥有独立变量。DTCG Format 2025.10描述了 token 的值、类型和引用方式,但不能据此认定这两个库已经实现完整的 DTCG 交换格式。该文档属于 Community Group Report,并非 W3C Standard。

先把三层分开

讨论覆盖范围时,需要分别检查:

  1. 基础尺度:颜色原值、字号、间距、尺寸、阴影、断点等。两者都大量承接 Tailwind,不能把这一层只算给某一方。
  2. 语义角色:品牌强调、弱化文字、浮层、表单、成功或危险等。这是默认 token 体系最值得直接比较的部分。
  3. 组件 Recipe:按钮如何变大、输入框如何显示错误、按下时是否缩放。很多细节写在组件样式中,未提升为全局 token。

例如 gap-4 表示从间距尺度取值;“表单行间距统一用多少”是项目规则;“按钮内部的图标间距取哪一档”属于组件 Recipe。这三件事相关,但不能互相替代。

变量、Utility class 与组件类:职责和来源分开看

token 保存设计值,class 将样式应用到元素;Tailwind 生成的 class 可以消费 shadcn 或 HeroUI 定义的 token。 因此,“来自 Tailwind”与“使用组件库的主题值”可以同时成立。下面将名称的职责与提供者分开对齐,范围仍是本文固定的 Tailwind v4 与两家源码快照。

职责shadcn/uiHeroUI v3怎样计入本文清单
语义值--primary--accent默认全局主题主表
Tailwind 颜色注册名--color-primary: var(--primary)--color-accent: var(--accent)桥接变量附表;引用不新增独立设计值
使用颜色的 Utilitybg-primary、text-primary、border-primarybg-accent、text-accent、border-accentclass,不加入变量数量
带条件的 Utilityhover:bg-primary/80hover:bg-accent-hover条件与修饰组合,不新增命名 token
组件 Recipe 类当前 Nova 的 cn-button、cn-button-variant-defaultbutton、button--primary组件样式选择器,不是颜色注册名自动生成的 Utility
React APIvariant="default"、classNamevariant="primary"、className前者选择 Recipe,后者传入类名;prop 本身不属于 token 或 class

例如 shadcn 的 --primary 经 @theme inline 的 --color-primary 注册后,bg-primary 使用 background-color: var(--primary);text-primary 则把同一颜色用于文字。一个颜色可以服务多个 CSS 属性,也就对应多个 Utility。普通 :root 中仅有 --primary 声明,并不会自动注册 bg-primary。Tailwind:Theme Variables、引用其他变量

class 也不总是读取某个命名 token:

示例实际含义为什么不能按 class 数量统计 token
p-4、gap-6默认按 --spacing 的 4 倍、6 倍计算不同属性可共享同一个尺度变量,不要求各有一个命名变量
flex、items-center设置布局方式与对齐不需要主题 token
status-disabledHeroUI 用 @utility 定义的自定义 Utility,读取 disabled-opacity 等变量类由 HeroUI 提供,不是 Tailwind 原生项;它消费的变量另列在主题表
w-[37px]使用任意值生成宽度规则没有新增命名 token;若写 w-[var(--site-width)],才会引用相应变量
hover:bg-primary/80hover: 限定条件,bg- 选择属性,/80 调整颜色 alpha不等于库新增了 --primary-hover;也不等于整个元素使用 opacity 0.8
md:p-6、dark:bg-background在断点或 dark 条件命中时应用 Utility条件可能依赖主题配置,但组合后的类名不是额外的主题声明

Theme Variable 声明的是可用设计值;Tailwind 再根据源码检测到的完整类名等构建输入生成相应规则,并非每声明一个颜色就把全部属性、状态和断点组合输出。Arbitrary values 还允许直接表达主题之外的值。因此,变量可以按固定文件穷举,class 更适合列出语法与代表例子。Tailwind:Utility classes、状态与条件、源码检测

最后,组件不必经过某个 Utility 才能读取 token。这两条路径都成立:

消费位置shadcn 当前 NovaHeroUI v3.2.6
自建元素上的 Utilitybg-primary → 背景规则 → --primarybg-accent → 背景规则 → --accent
默认 Button 的 Recipevariant="default" → cva 选择具名组件类 → Nova CSS 用 @apply bg-primary 展开声明 → 读取 --primaryvariant="primary" → buttonVariants 选择 button / button--primary → 将 --button-bg 接到 --accent → .button 直接读取局部变量

@apply 在构建时展开声明,不会把 bg-primary 再加进 DOM 的 class 属性。HeroUI 的 button--primary 也不意味着元素必须带上 bg-accent。两家都能组合 Utility 与具名组件规则,差异在 Recipe 的组织和 token 的消费位置,不能简化成“shadcn 用 class,HeroUI 用 token”。shadcn Button、Nova CSS、HeroUI Button 类名映射、Button CSS

覆盖范围对照

按本文固定源码,shadcn 的主题模板直接声明 32 个不同名称,加上 7 个圆角尺度输出,共 39 项;HeroUI 的默认变量文件声明 89 个不同名称,加上 8 个圆角尺度、21 个缓动、3 个动画和 2 个层叠参数,共 123 项。 这里统计的是指定文件暴露的全局命名入口,其中同时包含语义角色、派生状态、尺度输出和行为参数;39 / 123 不能解释为同一层级的基础设计值数量,更不能据此推算能力比例。

shadcn 的 32 项可以直接复核:18 个通用 UI 颜色 + 5 个图表色 + 8 个侧栏色 + 1 个根圆角。:root 声明这 32 项,.dark 覆写其中 31 个颜色,圆角沿用根值。它们不是 32 个 Primitive Token;18 个通用 UI 颜色本身也已按用途命名。本文固定清单的数字不推广为所有官方主题或已有项目必须遵循的变量总数。

下面的主表逐项列出两家的默认全局主题名称;Tailwind 桥接映射和原生主题变量分别放在文末附录,不打断后续机制比较。颜色对应按用途排列;— 只表示固定主题文件中没有独立同类名称,不能按功能缺失计分。相关外观可能已经由 Tailwind 或组件 Recipe 实现,也可以由项目扩展。

  • 主表保留语义变量和尺度输出;接入 Tailwind 的桥接别名单独列在附录映射表,不与同一角色重复计数。
  • 比较边界是 shadcn 的默认 Theming scaffold,以及 HeroUI 的 default/variables.css、shared/theme.css、base/base.css。两者共用的 Tailwind 调色板、字号、间距和断点另列在附录原生主题表;可选 Typeset、HeroUI Pro 和组件内部局部变量不混入默认全局主题。
  • 对应不等于无损替换:primary 对 accent、muted-foreground 对 muted、input 对 field-border 都要结合组件检查。radius 行写出根值倍数,具体像素与组件选档见后文。
用途与对应关系shadcn/uiHeroUI v3
基础色·纯白—--white
基础色·纯黑—--black
基础色·近白—--snow
基础色·近黑—--eclipse
页面·页面背景--background--background
页面·主要内容--foreground--foreground
页面·页面次级背景—--background-secondary
页面·页面三级背景—--background-tertiary
页面·反向背景—--background-inverse
容器·基础表面--card--surface
容器·表面内容--card-foreground--surface-foreground
容器·表面 hover—--surface-hover
容器·次级表面—--surface-secondary
容器·次级表面内容—--surface-secondary-foreground
容器·三级表面—--surface-tertiary
容器·三级表面内容—--surface-tertiary-foreground
浮层·浮层表面--popover--overlay
浮层·浮层内容--popover-foreground--overlay-foreground
浮层·遮罩—--backdrop
品牌主行动·背景--primary--accent
品牌主行动·内容--primary-foreground--accent-foreground
品牌主行动·hover—--accent-hover
品牌主行动·soft 背景—--accent-soft
品牌主行动·soft 内容—--accent-soft-foreground
品牌主行动·soft hover—--accent-soft-hover
中性填充·背景--secondary--default
中性填充·内容--secondary-foreground--default-foreground
中性填充·hover—--default-hover
中性填充·soft 背景—--default-soft
中性填充·soft 内容—--default-soft-foreground
中性填充·soft hover—--default-soft-hover
交互强调·背景(同名异义)
HeroUI 的 accent 已列在品牌主行动;此处按组件选择中性或表面角色。
--accent—
交互强调·内容--accent-foreground—
弱化内容·背景
HeroUI 按实际表面层级选择;其 muted 表示文字色。
--muted—
弱化内容·文字--muted-foreground--muted
链接·文字
HeroUI 默认引用 foreground;shadcn 按 Recipe 选择颜色。
—--link
分段控件·分段选中背景—--segment
分段控件·分段选中内容—--segment-foreground
成功·基础色—--success
成功·实色内容—--success-foreground
成功·hover—--success-hover
成功·soft 背景—--success-soft
成功·soft 内容—--success-soft-foreground
成功·soft hover—--success-soft-hover
警告·基础色—--warning
警告·实色内容—--warning-foreground
警告·hover—--warning-hover
警告·soft 背景—--warning-soft
警告·soft 内容—--warning-soft-foreground
警告·soft hover—--warning-soft-hover
危险·基础色--destructive--danger
危险·实色内容—--danger-foreground
危险·hover—--danger-hover
危险·soft 背景—--danger-soft
危险·soft 内容—--danger-soft-foreground
危险·soft hover—--danger-soft-hover
边界·通用边框--border--border
边界·次级边框—--border-secondary
边界·三级边框—--border-tertiary
边界·分隔线—--separator
边界·次级分隔线—--separator-secondary
边界·三级分隔线—--separator-tertiary
边界·焦点颜色--ring--focus
表单·背景—--field-background
表单·文字—--field-foreground
表单·占位文字—--field-placeholder
表单·hover 背景—--field-hover
表单·focus 背景—--field-focus
表单·边框(近似对应)--input--field-border
表单·hover 边框—--field-border-hover
表单·focus 边框—--field-border-focus
图表·系列色 1--chart-1—
图表·系列色 2--chart-2—
图表·系列色 3--chart-3—
图表·系列色 4--chart-4—
图表·系列色 5--chart-5—
侧栏·背景--sidebar—
侧栏·文字--sidebar-foreground—
侧栏·主强调--sidebar-primary—
侧栏·主强调内容--sidebar-primary-foreground—
侧栏·交互强调--sidebar-accent—
侧栏·交互强调内容--sidebar-accent-foreground—
侧栏·边框--sidebar-border—
侧栏·焦点--sidebar-ring—
圆角·根值
shadcn 默认 0.625rem;HeroUI 默认 0.5rem。
--radius--radius
圆角·xs—--radius-xs
×0.25
圆角·sm--radius-sm
×0.6
--radius-sm
×0.5
圆角·md--radius-md
×0.8
--radius-md
×0.75
圆角·lg--radius-lg
×1
--radius-lg
×1
圆角·xl--radius-xl
×1.4
--radius-xl
×1.5
圆角·2xl--radius-2xl
×1.8
--radius-2xl
×2
圆角·3xl--radius-3xl
×2.2
--radius-3xl
×3
圆角·4xl--radius-4xl
×2.6
--radius-4xl
×4
圆角·表单—--field-radius
尺寸·通用边框宽度—--border-width
尺寸·表单边框宽度—--field-border-width
尺寸·焦点分隔宽度—--ring-offset-width
尺寸·基础间距单位
双方都使用 Tailwind 间距尺度。
—--spacing
阴影·容器—--surface-shadow
阴影·浮层—--overlay-shadow
阴影·表单—--field-shadow
交互·禁用透明度—--disabled-opacity
交互·交互光标—--cursor-interactive
交互·禁用光标—--cursor-disabled
缓动·默认平滑—--ease-smooth
缓动·加速 / 二次—--ease-in-quad
缓动·加速 / 三次—--ease-in-cubic
缓动·加速 / 四次—--ease-in-quart
缓动·加速 / 五次—--ease-in-quint
缓动·加速 / 指数—--ease-in-expo
缓动·加速 / 圆形—--ease-in-circ
缓动·减速 / 二次—--ease-out-quad
缓动·减速 / 三次—--ease-out-cubic
缓动·减速 / 四次—--ease-out-quart
缓动·减速 / 五次—--ease-out-quint
缓动·减速 / 指数—--ease-out-expo
缓动·减速 / 圆形—--ease-out-circ
缓动·先加速后减速 / 二次—--ease-in-out-quad
缓动·先加速后减速 / 三次—--ease-in-out-cubic
缓动·先加速后减速 / 四次—--ease-in-out-quart
缓动·先加速后减速 / 五次—--ease-in-out-quint
缓动·先加速后减速 / 指数—--ease-in-out-expo
缓动·先加速后减速 / 圆形—--ease-in-out-circ
缓动·fluid 减速—--ease-out-fluid
缓动·线性—--ease-linear
动画与等待·快速旋转—--animate-spin-fast
动画与等待·骨架位移—--animate-skeleton
动画与等待·光标闪烁—--animate-caret-blink
动画与等待·骨架模式—--skeleton-animation
动画与等待·Tooltip 打开延迟—--tooltip-delay
动画与等待·Tooltip 关闭延迟—--tooltip-close-delay
滚动条·滑块别名—--scrollbar
滚动条·滑块—--scrollbar-thumb
滚动条·轨道—--scrollbar-track
滚动条·槽位—--scrollbar-gutter
滚动条·宽度—--scrollbar-width
滚动条·颜色组合—--scrollbar-color
层叠·浮层—--z-index-overlay
层叠·Toast—--z-index-toast

主表含 139 行:shadcn 的 39 个主题名称、HeroUI 的 123 个主题名称均已逐项列出。桥接表再覆盖其余 31 / 69 个别名,两张表合起来对应源码中的 70 / 192 个不同声明名,遗漏为 0。

证据入口:shadcn 默认主题源码、HeroUI 默认变量、共享映射、base.css。

数量与去重口径

数量可以统计,但必须固定文件范围、去重口径与层次。shadcn scaffold 有 70 个不同声明名:模板直接声明的 32 项、7 个圆角尺度输出,以及 31 个颜色桥接别名;去掉最后一类,就是上面的 39 个主题名称。

HeroUI 三份文件合计有 192 个不同声明名。其中 69 个只是同一角色接入 Tailwind 的别名:64 个颜色、3 个阴影、1 个表单圆角和 1 个表单边框宽度;去掉它们,得到 123 个主题名称。89 个默认变量中已经包含 accent-hover、accent-soft 等派生结果,不能将 89 解释为与 shadcn 模板同层的独立基础输入。比如 field-background 映射为 color-field,surface-shadow 映射为 shadow-surface,名字并非总是原名直接加前缀。

同一语义值在 light、dark 中重复声明,只计一次。focus 引用 accent、link 引用 foreground 这类用途不同的角色则保留,因为它们允许独立覆盖。颜色派生、圆角倍数和缓动曲线也保留为可检索名称;因此 123 不是“123 个互不相关的设计决定”。

仅看颜色,shadcn 是 31 个语义角色,HeroUI 是 64 个映射颜色角色,另有 white、black、snow、eclipse 四个基础色值。HeroUI 较长的总清单还包含 21 个缓动名称及滚动条等参数;不必将这些都理解为业务界面必须逐项配置的内容。默认变量的存在也不等于效果已经全局启用,例如 scrollbar 参数仍需要相应 Utility 或组件实际使用。

名称相同,语义可能不同

跨库迁移时,最容易出错的是逐字替换。

shadcn/ui 角色HeroUI 中较接近的角色注意事项
primary / primary-foregroundaccent / accent-foreground都适合主行动与主强调;需要检查实际使用位置
accent / accent-foreground根据场景使用 default、surface-hover、选中样式等shadcn accent 常用于菜单高亮、ghost hover 等,HeroUI accent 是品牌主强调,不能同名直译
muted-foregroundmuted两者都可用于弱化文字
muted按场景选择 surface 或 background 的某一层shadcn muted 常是弱化背景,HeroUI muted 是文字色;直接把它当背景会改变层次
cardsurface可作为起点;surface 的多个层级需要重新分配
popoveroverlay配对 foreground、shadow 和遮罩一起检查
destructivedangerHeroUI 另有 soft、hover 等角色
ringfocus焦点颜色之外,还要检查线宽、offset 和组件状态
input主要检查 field-border 等表单角色不能直接映射成 field-background;输入边框与背景是不同设计决策

映射表是迁移起点,不是两套体系的无损一一对应。依据分别见 shadcn Token Convention 与 HeroUI Colors。

配对命名也不保证颜色自动匹配。--primary-foreground 是另一项主题输入;修改 --primary,不会由 shadcn 自动计算出合适的文字色。某些角色在默认主题中数值相同,也应保留各自名称:今天 card 与 popover 都是白色,不表示未来必须同步更改。

这里有三个容易漏掉的细节:

  • --link 默认引用 --foreground,并非一定使用品牌蓝;--focus 默认引用 --accent,仍可独立覆盖。
  • --segment 是具体部件角色。它不能与 --surface-secondary 合并,也不能把所有选中态都推给它。
  • success、warning 的 foreground 默认使用深色 eclipse,accent、danger 则使用浅色 snow。状态色上的文字不应统一写死为白色。

上述角色说明依据 HeroUI Colors 和固定版本默认变量。--scrollbar-*、阴影、尺寸和动画参数在后文另列,不混进颜色角色。

默认视觉有何区别:深浅模式不是同一种反转

下面以固定文档中的 shadcn neutral 示例和 HeroUI 默认主题比较。数字仅代表这两个快照的取值;命名相同、某些值相同,都不要求主题始终保持相同外观。

比较对象shadcn light → darkHeroUI light → dark实际含义
页面背景oklch(1 0 0) → oklch(0.145 0 0)oklch(0.9702 0 0) → oklch(12% 0.005 285.823)HeroUI 的 light 默认页面与白色容器已经区分
主要内容oklch(0.145 0 0) → oklch(0.985 0 0)eclipse → snow配对内容色按模式调整
Card / surface白色 → oklch(0.205 0 0)白色 → oklch(0.2103 0.0059 285.89)内容表面有独立角色
Popover / overlay白色 → oklch(0.205 0 0)白色 → oklch(0.2103 0.0059 285.89)浮层与普通容器即使同值,也可以分别覆盖
primary / accent 背景及内容0.205 / 0.985 → 0.922 / 0.205(均为 oklch(L 0 0))accent 在两种模式均为 oklch(0.6204 0.195 253.83),内容色均为 snowshadcn neutral 主行动翻转明暗关系;HeroUI 默认主行动保持蓝色
secondary / defaultoklch(0.97 0 0) → oklch(0.269 0 0)oklch(94% 0.001 286.375) → oklch(27.4% 0.006 286.033)中性填充各自选择表面值
辅助文字muted-foreground:oklch(0.556 0 0) → oklch(0.708 0 0)muted:oklch(0.5517 0.0138 285.94) → oklch(70.5% 0.015 286.067)同一用途需要对应不同名称
危险基础色oklch(0.577 0.245 27.325) → oklch(0.704 0.191 22.216)oklch(0.6532 0.2328 25.74) → oklch(0.594 0.1967 24.63)危险角色按模式调整,不保证两家同色
通用 borderoklch(0.922 0 0) → 白色 / 10% alphaoklch(90% 0.004 286.32) → oklch(28% 0.006 286.033)shadcn dark 边框包含透明度
表单边框input:oklch(0.922 0 0) → 白色 / 15% alphafield-border 默认 transparent,field-border-width 默认 0px颜色与宽度需要一起比较
表单背景Nova Input 默认 transparent,dark 使用 input/30白色 → oklch(0.2103 0.0059 285.89)shadcn 此处是 Recipe,HeroUI 有 field-background 入口
焦点颜色ring:oklch(0.708 0 0) → oklch(0.556 0 0)focus 默认引用 accent,两种模式保持同一基础色焦点色不必等同品牌色
遮罩由具体组件 Recipe 决定黑色 alpha 0.5 → 0.6HeroUI 的 backdrop 是单独的全局角色

shadcn 的 secondary、muted、accent 在此示例中都为 light oklch(0.97 0 0)、dark oklch(0.269 0 0);secondary-foreground 与 accent-foreground 则为 light oklch(0.205 0 0)、dark oklch(0.985 0 0)。这些数值重合不应消除角色边界。HeroUI 的 surface-secondary、surface-tertiary 也是独立赋值的表面,不是从 surface 自动计算的一切层次。

证据:shadcn 固定主题、HeroUI 默认变量、Nova Input Recipe。

状态色怎样派生:透明度、混色和文字色是三件事

同一个 hover 需求,两家可以使用不同的计算规则和覆盖入口。先看并排关系,再核对 HeroUI 派生公式中的具体权重。

比较维度shadcn NovaHeroUI 默认体系修改影响
主行动 normalprimary 与 primary-foregroundaccent 与 accent-foreground两家都需要检查配对内容色
主行动 hoverRecipe 使用 primary/80accent-hover 混合 accent 与 accent-foreground背景透明度与向内容色混合会得到不同外观
soft 背景具体变体在 Recipe 中决定比例,例如 destructive/10accent、success、warning、danger 等有独立 soft 角色HeroUI 提供了集中覆盖入口;shadcn 可修改 Recipe 或新增变量
soft 内容组件按角色与变体选择部分 soft-foreground 从状态色与 foreground 派生不应将所有 soft 内容统一写成基础色或白色
pressed由组件 Recipe 决定位移、背景等Button 局部 pressed 值可复用 hover,并缩放两家都不能仅凭全局颜色清单推导按下行为

在本次 Nova 样式中,默认 Button 使用 bg-primary,hover 使用 bg-primary/80。Tailwind 将后者编译为 primary 与 transparent 的混合;它与 HeroUI 将 accent 混向 accent-foreground 的规则不同。把相同的蓝色分别填进两边,不会得到相同的 hover。

如果要把主按钮 hover 单独改成固定颜色,HeroUI 可以覆盖 --accent-hover;Nova 则没有默认的 --primary-hover 入口,需要修改 Button 的 hover Recipe,或者新增项目 token 并让 Recipe 引用。反过来,shadcn 把规则保存在项目源码里,组件某个特殊变体需要完全不同的状态时,可以直接修改它。两种方式的差别是统一变化的控制点放在哪里。Nova 样式

派生公式与深浅模式

以下取自同一份默认变量,A 表示 --accent,AF 表示 --accent-foreground,F 表示 --foreground;表内写法用于解释权重,不是可直接复制的 CSS。

派生角色light 规则dark 规则观察重点
accent-hover在 Oklab 中混合 90% A、10% AF相同向 foreground 靠近,不保证总是“变暗”
accent-soft15% A 与 transparent 混合12% A 与 transparent 混合是半透明背景,会受承载表面影响
accent-soft-hover20% A 与 transparent 混合16% A 与 transparent 混合hover 与 normal 各有独立角色
accent-soft-foreground70% A、30% F 混色80% A、30% F 混色文字不是简单复用 A;比例合计超出 100% 时 CSS 会归一化
surface-hover92% surface、8% surface-foreground相同跟随表面与文字的关系
background-secondary / tertiarybackground 分别混入 4% / 8% foreground相同派生页面层次;与显式赋值的 surface 层次分开

success、warning、danger 的 soft 角色遵循类似结构,但不能假定比例完全一样。例如 light 的 warning-soft-foreground 使用 80% warning 与 70% foreground;danger 的 soft 背景在 dark 下仍是 15%,并非所有状态都沿用 accent 的 12%。需要精确复刻时应读对应公式,不能根据名字推算。默认变量源码、CSS Color 5:混色比例归一化

默认还有可选的 data-vibrant-palette="true" 分支,会将四组彩色 soft foreground 改为 92% 状态色与 8% foreground 的混色。它改变的是轻色背景上的文字表达,不是把整套主题所有颜色都增强饱和度;其选择器同时匹配 light/dark 标记,局部使用也要核对所在元素。对应实现

Derived colors do not guarantee accessible contrast. A token pair must be checked in the context where the component actually renders. 半透明 soft 背景、disabled opacity、文字大小和真实承载背景都参与结果;除了颜色,还要检查键盘、标签和状态说明。WCAG 2.2

圆角、边框、阴影与密度:相同类名并非相同尺度

圆角公式逐档对照

本次 shadcn Theming 的根值是 --radius: 0.625rem,HeroUI 是 --radius: 0.5rem。以下像素值假设根字号 16px;这是尺度比较,不是某个组件最终一定使用的半径。

Tailwind 半径入口shadcn:根值的倍数 / 默认像素HeroUI:根值的倍数 / 默认像素
xs此映射未重定义,继续取 Tailwind 对应值×0.25 / 2px
sm×0.6 / 6px×0.5 / 4px
md×0.8 / 8px×0.75 / 6px
lg×1 / 10px×1 / 8px
xl×1.4 / 14px×1.5 / 12px
2xl×1.8 / 18px×2 / 16px
3xl×2.2 / 22px×3 / 24px
4xl×2.6 / 26px×4 / 32px
field核心主题没有独立角色field-radius 默认 ×1.5 / 12px

旧 shadcn 教程里常见 radius - 4px、radius - 2px 的公式,与本文快照不同。已有项目需要查自己的 CSS,不能把当前文档公式当作已安装组件的事实。shadcn 固定主题、HeroUI 半径映射

组件仍会选择档位或限制上限:Nova 默认 Button 与 Input 都用 radius-lg,HeroUI Button 用 rounded-3xl,Input 用 rounded-field;HeroUI Card 还使用 min(32px, var(--radius-3xl))。因此,将两边 --radius 都设成 8px,不会让按钮、卡片和输入框获得相同圆角。需要比较的是 根值 × 尺度公式 × Recipe 选择。Nova、HeroUI Card

其他全局参数分别能做什么

参数shadcn 核心主题 / RecipeHeroUI 默认入口与边界
通用与表单边框宽度Recipe 的 border、ring 等规则border-width = 1px;field-border-width = 0px。不是所有组件都读取通用宽度变量
语义阴影核心清单不含 card-shadow、popover-shadow,按 style 选 Tailwind 阴影或 ringsurface-shadow、overlay-shadow、field-shadow;对应 shadow-surface、shadow-overlay、shadow-field
深色阴影由项目主题与 Recipe 决定surface/field 默认透明,overlay 改为轻微白色 inset 边界;不只是 light 阴影变黑
基础间距Tailwind --spacing 与尺寸 Utilities同样使用 Tailwind;默认显式声明 spacing = 0.25rem
默认控件密度Nova Button、Input 为 h-8,并选择各自 padding/gapButton 默认 h-10,md 断点 h-9;Input 采用 padding 等 Recipe
disabled组件内 opacity-50 等disabled-opacity = 0.5、cursor-interactive = pointer、cursor-disabled = not-allowed
焦点分隔组件 ring/outline Recipering-offset-width 默认 2px;field focus 的 Utility 明确使用 0 offset
缓动Tailwind 与组件的 transition/duration额外导出 ease-smooth、ease-in/out/in-out 等曲线;组件时长仍在 Recipe 中
动画与等待组件样式及动画依赖spin-fast、skeleton、caret-blink;skeleton-animation = shimmer,Tooltip delay = 1500ms、close-delay = 500ms
滚动条ScrollArea 等具体组件或项目样式scrollbar-thumb/track/width/gutter/color;通过相应 Utility 作用于滚动区域
层叠顺序组件 Recipe 的 z-index 等base.css 的 z-index-overlay = 100000,toast = overlay + 1;不是任意页面元素的全站层级规范

这些是可控范围,不是所有参数都会随意传播到所有元素。例如 HeroUI Button 的 100ms 背景过渡和 250ms transform 仍写在组件里,改 easing 不会自动统一 duration。把 spacing 从 0.25rem 改成 0.2rem,也无法同比缩小那些直接写为 rem、px 或最小命中范围的值。HeroUI 默认变量、共享映射、状态 Utilities、base.css

排版、间距和动效到底算不算“有”

两者都有能力完成排版、留白、阴影与动效,区别是这些决策放在哪里。

shadcn 的核心主题清单没有把所有字号、间距、时长抽成网站级语义角色,但 Tailwind 尺度与组件 Recipe 已经在使用这些值。当前 shadcn/typeset 还提供独立的内容排版体系:body、heading、mono 字体,以及 size、leading、flow 三个主要阅读节奏控制点。它适合文档、文章、聊天内容,不能把 shadcn 概括成“没有排版体系”。

HeroUI 这两个主题文件也没有独立的 --font-*、--text-* 全套正文角色。它的主题页虽然把 typography 列入广义基础变量,但核对具体实现后,应将这里的能力归于 Tailwind 与组件 Recipe;不要据此推导出一个已经配置好的 display、heading、body、caption 产品排版规范。

图表与侧栏:专用角色及扩展方式

chart-1 至 chart-5 能让数据系列颜色跟随主题,图表配置引用它们后,深浅模式只需调整相应值。不过系列名、legend、单位、正负、阈值与顺序色阶仍属于图表设计。ChartConfig 还能为某个系列直接提供 color,或提供 light/dark 两个值;ChartStyle 将这些颜色写成局部 --color-系列名。因此默认五色既不是系列上限,也不是只能画五种数据。Chart 文档

sidebar 分组的价值在于隔离导航区域。正文 primary 改了,sidebar-primary 可以保持原值;sidebar-accent 则服务菜单的 hover/active 等强调。它不等于给 Card 增加第二档 surface,也不能与 chart 系列共用一套命名。HeroUI 核心主题没有这两组入口,需要项目按实际需求增加并让自建图表、侧栏消费。Sidebar 文档

token 怎样真正影响组件

前面的表比较命名与计算;这里沿着同一个控件追踪这些值怎样进入最终样式。shadcn 栏固定为 Nova,HeroUI 栏固定为 v3.2.6 默认样式。

层次shadcn 主行动 ButtonHeroUI 主行动 Button
语义输入primary、primary-foregroundaccent、accent-foreground
状态计算hover 比例在 Recipe 中,读取 primary/80默认主题先计算 accent-hover
Tailwind 接入color-primary 等映射供 bg-primary 使用color-accent-hover 等映射供对应 Utility 使用
组件消费项目持有的 Recipe 组合颜色、尺寸与状态类primary 变体将 button-bg、button-bg-hover、button-fg 等局部值接到 accent 系列
按下反馈Recipe 使用位移等状态规则Button 复用 hover 背景,并按尺寸缩放

HeroUI 当前没有统一的全局 *-pressed 颜色家族;Button 的局部变量不能泛化为所有组件共享的稳定契约。Nova 的 destructive Badge 也会用同一危险色组合轻色背景、hover 与 focus ring,说明状态外观可以在 Recipe 内表达。源码入口:Nova Recipe、HeroUI Button。

同一个 Button:逐个状态对照

以下 shadcn 栏固定为 Nova style;它不是所有历史 shadcn Button 的共同实现。HeroUI 栏固定为 v3.2.6 默认 Button。

状态或决定shadcn Nova ButtonHeroUI Buttontoken 能控制到哪里
主行动 normalbg-primary、text-primary-foregroundprimary 变体将 --button-bg、--button-fg 接到 accent 与 accent-foreground都能集中改主色与配对内容色
主行动 hoverbg-primary/80--button-bg-hover 接到 accent-hovershadcn 的比例在 Recipe;HeroUI 的派生结果有单独入口
pressedactive:not-aria-[haspopup]:translate-y-px背景复用 accent-hover;默认缩放 0.97,sm 为 0.98、lg 为 0.96两者的位移或缩放仍属于 Recipe,不由颜色 token 决定
focus-visibleborder-ring、ring-ring/50 与 3px ringstatus-focused 使用 focus 色、2px ring,offset 默认 2pxring / focus 都只是部分输入,线宽与规则也要对齐
disabled基础组件设置 opacity-50、禁止 pointer eventsstatus-disabled 读取 disabled-opacity、cursor-disabled,并禁止 pointer eventsHeroUI 多了集中透明度和光标入口
次行动secondary 背景与 foregroundsecondary 使用 default 背景,却将文字接到 accent-soft-foreground变体名称相同也不保证配色相同
危险行动Nova destructive 变体使用 destructive 的轻色填充、文字与状态规则danger 为实色;danger-soft 是另一变体destructive → danger 的角色映射不等于视觉一致

hover:bg-primary/80 只改变背景的 alpha;disabled:opacity-50 会同时淡化整个按钮及其文字、图标。两者不能互换。Nova 的 ghost hover 还直接使用 muted,说明 Theming 的角色解释与某个 style 的实际选择需要分别检查。Nova Recipe、shadcn 基础 Button、HeroUI Button、HeroUI 状态 Utilities

因此,若需求是“所有可点击控件的 hover 都使用同一种算法”,还要检查哪些 Recipe 真正消费这组变量。HeroUI 有更多集中入口,也不是一个 token 能覆盖全部组件:outline Button 就有自己的 default 混色规则。

同一个 Input:逐个状态对照

状态或决定shadcn Nova InputHeroUI primary Input
normal 背景默认 transparent,dark 为 input/30bg-field → field-background;文字与占位也有独立 field 角色
边框border-inputfield-border;宽度由 field-border-width 决定,默认 0px
hoverNova 没有单独的 field hover 颜色角色field-hover 背景与 field-border-hover 边界
focusfocus-visible 使用 ring/50、3px ring 与 border-ringfocus 时使用 field-focus、field-border-focus,并用 focus 色显示 2px ring
invalidaria-invalid 的 destructive 边框与 ring;dark 下透明度不同data-invalid 触发 danger outline;获得焦点后显示 danger ring
disabled基础 opacity-50;Nova 另使用 input/50,dark 为 input/80status-disabled 读取全局 disabled-opacity 与 cursor-disabled
圆角Recipe 使用 radius-lgrounded-field → field-radius
阴影由所选 style 决定shadow-field → field-shadow;secondary Input 会去掉该阴影并改用 default 背景

field-border 默认 transparent,且边框宽度是 0px,所以仅修改边框颜色不一定能看见边框。要在全局给 HeroUI 输入控件加 1px 边框,应在根主题同时设置 --field-border-width: 1px 与 --field-border,再检查 hover/focus。与此相对,修改 shadcn 的 --input 是在改变输入边界角色;它并不是统一的输入背景开关。

映射中的 var(--field-border, var(--border)) 也不表示两个颜色叠加:field-border 已经存在且值为 transparent 时,就不会采用 border 的 fallback。同理,0px 是有效宽度,不会因为没有可见边框就自动退回通用的 1px。

HeroUI 的 field 系列也不会约束每个变体:上表的 secondary Input 改走 default 系列,是明确的 Recipe 选择。想让“所有表单都统一换一种背景”,必须先确定实际用了哪些 variants。shadcn Input、Nova 样式、HeroUI Input

主题修改:接入方式、覆盖范围与扩展成本

先比较共同的 Tailwind 接入,再比较相同修改需求在两家分别要改哪里;最后使用双份示例验证接线。

token 如何接入 Tailwind:两家共享机制,维护边界不同

在 Tailwind v4 中,运行时语义变量与 Utility 的主题名称是两层:

过程shadcnHeroUI
定义语义值项目 CSS 内的 --primary 等styles 包的 --accent 等,项目可以覆盖
注册 Utility 入口@theme inline 中的 --color-primary: var(--primary)共享 theme.css 的 --color-accent: var(--accent)
组件使用当前 Nova 用具名 Recipe 类,通过 @apply 展开 bg-primary 等 Utility自建元素可用 bg-accent 等 Utility;Button 用具名类和局部变量直接消费语义值
主题切换在匹配的选择器中覆写语义值同样覆写语义值;默认提供 light/dark class 与 data-theme 选择器
更新责任主题、组件 Recipe 的副本由项目持有styles 包负责默认主题与 Recipe;项目维护覆盖与自定义主题

HeroUI 有一个容易按名字猜错的入口:--color-field 映射到 --field-background,因此对应的是 bg-field,不是自动生成 bg-field-background。阴影则是 --shadow-field → --field-shadow,生成 shadow-field。注册名与被引用的语义变量不必同名。HeroUI 共享映射

下面是两套体系机制相同的地方;这是说明映射的片段,分别放在各自已配置的主题文件里:

css
/* shadcn 项目 */
@theme inline {
  --color-primary: var(--primary);
  --color-primary-foreground: var(--primary-foreground);
}

/* HeroUI 已经提供此映射,此处仅展示对应关系。 */
@theme inline {
  --color-accent: var(--accent);
  --color-accent-foreground: var(--accent-foreground);
}

inline 让生成的 Utility 使用被引用的值,例如 background-color: var(--primary),从而在消费元素的作用域中读取 primary。它不是把主题编译成固定颜色:运行时修改变量仍会影响组件。当前这些颜色值是完整的 oklch(...) 等 CSS color;不要照搬旧版 HSL 片段再套一层 hsl(var(--primary))。shadcn Theming、Tailwind:引用其他变量

主题覆盖的实际边界:全局、局部与组件级

全局换色与局部换色不是同一个 CSS 问题

全局主题切换时,两者都可以保留 Recipe,只改变语义值。HeroUI 默认 CSS 匹配 .light、.dark 及对应 data-theme;shadcn 常用 .dark 覆写。CSS 本身不会替应用保存用户偏好、处理系统模式或避免首屏闪烁,这些属于主题切换集成。HeroUI Theming

覆盖还受 cascade layer 影响。HeroUI 将默认样式组织在 theme、base、components、utilities 等层中;本文的全局覆盖示例放在 imports 后的普通 CSS 中。对于同一元素上匹配的普通声明,未分层声明优先于分层声明。因此只在未分层 :root 写 light 主色,可能同时压过包内根元素的 dark 默认值;应像示例那样补上对应 dark 覆盖。若把覆盖放回 layer,则要同时核对层顺序、选择器与源码顺序,不能只看“写在后面”。HeroUI CSS 层入口

局部主题还有一条关键限制:CSS custom properties containing references are resolved where they are declared; inheriting the result does not re-run the expression against a child’s overrides. 例如根部定义了 --accent-hover: color-mix(... var(--accent) ...),子区域只覆盖 accent,它可能仍继承根部已经计算的 hover。@theme inline 解决 Utility 的引用入口,不会重新计算任意祖先层的派生变量。

若在现有 HeroUI 项目中增加一个局部品牌区域,下面这段应追加在样式导入之后;normal 与 hover 同时在区域内声明:

css
.brand-zone {
  --accent: oklch(0.55 0.18 260);
  --accent-foreground: oklch(0.99 0 0);
  --accent-hover: color-mix(
    in oklab, var(--accent) 90%, var(--accent-foreground) 10%
  );
}

这只覆盖 primary Button 所需的三项。如果区域内还用 accent-soft、focus 等派生角色,也应在该区域重声明相应依赖,或采用完整自定义主题;不能把这个片段称为完整品牌主题。CSS Variables:computed value 与继承、Tailwind inline 的适用范围

中间别名也要检查:当前 HeroUI Input 的 border-width 直接读取 --border-width-field,该别名再引用 field-border-width。在控件自身仅覆盖 --field-border-width,可能仍然继承根部已解析的别名;局部修改时应同时在该作用域重声明 --border-width-field: var(--field-border-width)。根主题上的全局覆盖可以正常传播。Input 源码、映射源码

shadcn 的 primary/80 通常直接在消费处引用 primary,没有额外的祖先 primary-hover 计算链。但只覆写颜色也不能隔离 .dark:外层 dark 仍可能让内层组件的 dark:* Recipe 命中。局部 light 区域、主题预览卡片和多品牌混排,需要同时核对变量作用域与 mode 选择器。

浮层还可能通过 Portal 挂到局部区域之外。若浮层 DOM 没有位于 .brand-zone 内,它不会因为触发按钮在里面就自动继承局部 token;应检查最终 DOM,并按组件能力将主题作用于实际浮层容器。这是 CSS 继承和组件挂载位置共同带来的工程约束。

覆盖 token、修改 Recipe,分别应该在什么时候用

需求shadcn 的操作HeroUI 的操作
全站主行动换品牌色改 primary 与 primary-foreground,检查相关 Recipe改 accent 与 accent-foreground,再检查派生角色
主行动 hover 保留固定色新增 primary-hover,并改 Button 的 hover 引用覆盖 accent-hover,核对实际消费范围
只给输入控件单独换圆角为项目增加 field 半径并调整 Input 等 Recipe改 field-radius,再核对各个变体
所有内容容器统一改阴影增加项目阴影角色,并让相关 Recipe 引用改 surface-shadow;不影响走 overlay-shadow 的浮层
仅一个 Button 的特殊外观className 或项目持有的变体className、额外 BEM 类,或局部覆盖;修改内部变量前核对源码
增加业务语义,如 overdue增加项目变量与使用规则同样需要项目扩展;danger 不会自动等于业务中的“逾期”

HeroUI 的 --button-bg 等局部变量说明了当前 Recipe 的实现,但不应当成与 accent 同等级、保证长期稳定的公共契约。shadcn 的源码可直接修改,也意味着新增 token 后必须自己保证组件继续消费它:只声明 --primary-hover,不会自动替换已有 hover:bg-primary/80。

同一需求的双份扩展示例

目标是“品牌按钮有独立 hover,成功提示能随主题切换”。下面两段分别追加到已有 shadcn/Tailwind v4 项目和已有 HeroUI v3/Tailwind v4 项目的全局 CSS,不能把两段整体叠进同一项目。它们演示 token 与 Utility 的接线,不是两家 Button 的源码副本。

shadcn:新增变量并接入 Recipe

css
:root {
  --primary: oklch(0.55 0.18 260);
  --primary-foreground: oklch(0.99 0 0);
  /* 以下三项是本例的项目扩展。 */
  --primary-hover: oklch(0.48 0.17 260);
  --success: oklch(0.35 0.08 150);
  --success-foreground: oklch(0.99 0 0);
}

.dark {
  --primary: oklch(0.76 0.12 260);
  --primary-foreground: oklch(0.18 0.02 260);
  --primary-hover: oklch(0.82 0.10 260);
  --success: oklch(0.80 0.12 150);
  --success-foreground: oklch(0.18 0.02 150);
}

@theme inline {
  --color-primary-hover: var(--primary-hover);
  --color-success: var(--success);
  --color-success-foreground: var(--success-foreground);
}

.token-primary {
  @apply bg-primary text-primary-foreground hover:bg-primary-hover;
}

.token-success {
  @apply bg-success text-success-foreground;
}

这里的 .token-primary 是一个独立教学类。实际 shadcn Button 应将原 hover 类改为 hover:bg-primary-hover,再检查 className 合并规则;不要只增加变量就预期 Button 自动变化。

HeroUI:覆盖已有变量与派生结果

css
:root,
[data-theme="light"] {
  --accent: oklch(0.55 0.18 260);
  --accent-foreground: oklch(0.99 0 0);
  --accent-hover: oklch(0.48 0.17 260);
  --success: oklch(0.35 0.08 150);
  --success-foreground: oklch(0.99 0 0);
}

.dark,
[data-theme="dark"] {
  --accent: oklch(0.76 0.12 260);
  --accent-foreground: oklch(0.18 0.02 260);
  --accent-hover: oklch(0.82 0.10 260);
  --success: oklch(0.80 0.12 150);
  --success-foreground: oklch(0.18 0.02 150);
}

.token-primary {
  @apply bg-accent text-accent-foreground hover:bg-accent-hover;
}

.token-success {
  @apply bg-success text-success-foreground;
}

HeroUI 已经注册 accent-hover 与 success 系列的 Utility,不必再写一遍 @theme inline。已有 primary Button 也已消费 accent-hover;本例仍使用独立教学类,以便与 shadcn 检查相同的 HTML:

html
<button class="token-primary" type="button">保存更改</button>
<p class="token-success" role="status">已保存</p>

在独立页面中分别测试:light 下按钮背景为 oklch(0.55 0.18 260),hover 为 oklch(0.48 0.17 260);根元素增加 dark 后,分别变为 oklch(0.76 0.12 260) 与 oklch(0.82 0.10 260)。成功提示应切换为各自的 success/foreground 配对。此例只对照颜色与消费链,实际按钮仍须具备合适的尺寸与 focus indicator。

本轮以 Tailwind 4.3.3、HeroUI styles 3.2.6 编译上述两份扩展,并在 Chromium 中核对了两种模式的 normal、hover 与 success 配对,计算样式符合这些值。同时验证了局部 accent 覆盖的继承差异,以及全局 field 边框从 0px 改为 1px 的结果。

一般项目够不够用

以常规组件和深浅色主题为起点,两者通常都够。 这是一项工程判断,前提是允许团队添加少量项目角色与页面规则,而不是要求每项设计决定都必须在库的默认清单中找到同名 token。

项目需求shadcn 起点HeroUI 起点优先验证的缺口
博客、文档站、知识库Tailwind 与可选 TypesetTailwind 与组件 Recipe中文字体、正文宽度、代码、表格和搜索路径
常规产品官网语义主题与组件 Recipe语义主题与组件 Recipe栅格、内容顺序、图像与页面模板
SaaS、设置中心、业务表单现有角色;成功、警告等按需扩展较多 field 与状态入口验证流程、错误恢复、真实状态与密度
数据分析后台chart-1 至 chart-5,ChartConfig 可扩展项目自行定义图表角色系列、顺序、阈值与数据含义
多品牌、多租户、密度切换项目维护变量、Recipe 与模式规则在已有全局入口之上维护应用主题brand、mode、density 的组合及覆盖传播
作品集、活动与视觉叙事token 与 Recipe 可表达设计值token 与 Recipe 可表达设计值艺术方向、构图、图像及动效编排

如果已经采用 HeroUI,并且喜欢其组件视觉,不值得仅因 shadcn 有 chart 或 sidebar 命名就迁移。给项目补这两类角色通常更直接。反过来,已有 shadcn 项目只缺 success、warning,也通常可以扩展现有主题,不必更换组件库。

如果几乎每个组件都需要覆盖圆角、间距、状态和阴影,应重新评估默认 Recipe 是否符合项目。默认 token 少,不自动意味着工作多;默认变量多,也不能弥补视觉方向不匹配。

用三个真实页面验证“够用”

选一页信息展示、一页复杂表单、一页列表或数据页,使用真实中文、长标题、长数字与真实状态:

  1. 定义页面所需的角色,记录默认已有、可复用和需要新增的项目项。
  2. 同时检查深浅色;逐项看文字层级、表面、边框、焦点、hover、invalid、disabled、pending、成功与失败反馈。
  3. 把重复覆盖写进项目 Recipe 或 Semantic Token。只出现一次的装饰不必立即提升为全局契约。
  4. 改一次主强调色与基础圆角,确认预期组件响应;另改一次表单外观或图表色,确认没有误伤无关角色。
  5. 检查完整任务路径:键盘可操作,错误可理解,浮层不遮挡关键操作,长内容仍可读。

通过标准是关键页面能复用同一组规则、缺口可解释且集中、主题修改能传播到预期位置。不要凭变量数量估算“覆盖 90%”;没有项目需求清单就不存在可核查的覆盖率。

附录:Tailwind 变量索引与补充示例

这里保留完整映射与基础主题清单,供查名和排错。理解两家机制时,可以先沿正文比较顺序阅读,需要查具体名称时再回到本节。

Tailwind 桥接变量对照

这些变量由两家主题注册进 Tailwind;它们不是 Tailwind 默认调色板自带的颜色。表内箭头表示“桥接名称 → 主表里的语义变量”,不是两个独立设计值。这张表仍列变量名称,bg-primary、bg-accent 等消费类见前文变量与 class 分层,不与变量混计。

用途shadcn/ui 的 Tailwind 映射HeroUI 的 Tailwind 映射
页面·页面背景--color-background → --background--color-background → --background
页面·主要内容--color-foreground → --foreground--color-foreground → --foreground
页面·页面次级背景—--color-background-secondary → --background-secondary
页面·页面三级背景—--color-background-tertiary → --background-tertiary
页面·反向背景—--color-background-inverse → --background-inverse
容器·基础表面--color-card → --card--color-surface → --surface
容器·表面内容--color-card-foreground → --card-foreground--color-surface-foreground → --surface-foreground
容器·表面 hover—--color-surface-hover → --surface-hover
容器·次级表面—--color-surface-secondary → --surface-secondary
容器·次级表面内容—--color-surface-secondary-foreground → --surface-secondary-foreground
容器·三级表面—--color-surface-tertiary → --surface-tertiary
容器·三级表面内容—--color-surface-tertiary-foreground → --surface-tertiary-foreground
浮层·浮层表面--color-popover → --popover--color-overlay → --overlay
浮层·浮层内容--color-popover-foreground → --popover-foreground--color-overlay-foreground → --overlay-foreground
浮层·遮罩—--color-backdrop → --backdrop
品牌主行动·背景--color-primary → --primary--color-accent → --accent
品牌主行动·内容--color-primary-foreground → --primary-foreground--color-accent-foreground → --accent-foreground
品牌主行动·hover—--color-accent-hover → --accent-hover
品牌主行动·soft 背景—--color-accent-soft → --accent-soft
品牌主行动·soft 内容—--color-accent-soft-foreground → --accent-soft-foreground
品牌主行动·soft hover—--color-accent-soft-hover → --accent-soft-hover
中性填充·背景--color-secondary → --secondary--color-default → --default
中性填充·内容--color-secondary-foreground → --secondary-foreground--color-default-foreground → --default-foreground
中性填充·hover—--color-default-hover → --default-hover
中性填充·soft 背景—--color-default-soft → --default-soft
中性填充·soft 内容—--color-default-soft-foreground → --default-soft-foreground
中性填充·soft hover—--color-default-soft-hover → --default-soft-hover
交互强调·背景(同名异义)--color-accent → --accent—
交互强调·内容--color-accent-foreground → --accent-foreground—
弱化内容·背景--color-muted → --muted—
弱化内容·文字--color-muted-foreground → --muted-foreground--color-muted → --muted
链接·文字—--color-link → --link
分段控件·分段选中背景—--color-segment → --segment
分段控件·分段选中内容—--color-segment-foreground → --segment-foreground
成功·基础色—--color-success → --success
成功·实色内容—--color-success-foreground → --success-foreground
成功·hover—--color-success-hover → --success-hover
成功·soft 背景—--color-success-soft → --success-soft
成功·soft 内容—--color-success-soft-foreground → --success-soft-foreground
成功·soft hover—--color-success-soft-hover → --success-soft-hover
警告·基础色—--color-warning → --warning
警告·实色内容—--color-warning-foreground → --warning-foreground
警告·hover—--color-warning-hover → --warning-hover
警告·soft 背景—--color-warning-soft → --warning-soft
警告·soft 内容—--color-warning-soft-foreground → --warning-soft-foreground
警告·soft hover—--color-warning-soft-hover → --warning-soft-hover
危险·基础色--color-destructive → --destructive--color-danger → --danger
危险·实色内容—--color-danger-foreground → --danger-foreground
危险·hover—--color-danger-hover → --danger-hover
危险·soft 背景—--color-danger-soft → --danger-soft
危险·soft 内容—--color-danger-soft-foreground → --danger-soft-foreground
危险·soft hover—--color-danger-soft-hover → --danger-soft-hover
边界·通用边框--color-border → --border--color-border → --border
边界·次级边框—--color-border-secondary → --border-secondary
边界·三级边框—--color-border-tertiary → --border-tertiary
边界·分隔线—--color-separator → --separator
边界·次级分隔线—--color-separator-secondary → --separator-secondary
边界·三级分隔线—--color-separator-tertiary → --separator-tertiary
边界·焦点颜色--color-ring → --ring--color-focus → --focus
表单·背景—--color-field → --field-background
表单·文字—--color-field-foreground → --field-foreground
表单·占位文字—--color-field-placeholder → --field-placeholder
表单·hover 背景—--color-field-hover → --field-hover
表单·focus 背景—--color-field-focus → --field-focus
表单·边框(近似对应)--color-input → --input--color-field-border → --field-border
表单·hover 边框—--color-field-border-hover → --field-border-hover
表单·focus 边框—--color-field-border-focus → --field-border-focus
图表·系列色 1--color-chart-1 → --chart-1—
图表·系列色 2--color-chart-2 → --chart-2—
图表·系列色 3--color-chart-3 → --chart-3—
图表·系列色 4--color-chart-4 → --chart-4—
图表·系列色 5--color-chart-5 → --chart-5—
侧栏·背景--color-sidebar → --sidebar—
侧栏·文字--color-sidebar-foreground → --sidebar-foreground—
侧栏·主强调--color-sidebar-primary → --sidebar-primary—
侧栏·主强调内容--color-sidebar-primary-foreground → --sidebar-primary-foreground—
侧栏·交互强调--color-sidebar-accent → --sidebar-accent—
侧栏·交互强调内容--color-sidebar-accent-foreground → --sidebar-accent-foreground—
侧栏·边框--color-sidebar-border → --sidebar-border—
侧栏·焦点--color-sidebar-ring → --sidebar-ring—
圆角·表单—--radius-field → --field-radius
尺寸·表单边框宽度—--border-width-field → --field-border-width
阴影·容器—--shadow-surface → --surface-shadow
阴影·浮层—--shadow-overlay → --overlay-shadow
阴影·表单—--shadow-field → --field-shadow

其中 --color-field 指向 --field-background;--shadow-surface 指向 --surface-shadow,不是机械地在原名之前加 color 或 shadow。field 的部分映射还带有 default、foreground、muted、border 等 fallback,精确表达式以共享主题源码为准。

Tailwind 原生主题变量完整清单

本表固定为 Tailwind CSS 4.3.3 的 theme.css,共 413 个当前声明名 + 6 个 Deprecated 名称 = 419 个。其中 26 个色族各 11 档,再加黑白两项,共 288 个颜色;其余是字体、尺寸、阴影、动效等基础尺度。为便于查找,每个色族和相关尺度合并一行,但每一个实际声明名都明确写出。

下表是两家叠加主题之前的 Tailwind 基础清单。相同名称可以被库或项目覆写,因此不能把 419 直接加到 39 或 123 上,也不能据此认定两家的最终默认值都相同。

分类 / 名称数量变量名(全部列出)在比较中怎样理解
调色板·red / 11--color-red-50、--color-red-100、--color-red-200、--color-red-300、--color-red-400、--color-red-500、--color-red-600、--color-red-700、--color-red-800、--color-red-900、--color-red-950两家共用的基础色阶;语义主题可以引用它们,也可以直接使用自定义颜色。
调色板·orange / 11--color-orange-50、--color-orange-100、--color-orange-200、--color-orange-300、--color-orange-400、--color-orange-500、--color-orange-600、--color-orange-700、--color-orange-800、--color-orange-900、--color-orange-950两家共用的基础色阶;语义主题可以引用它们,也可以直接使用自定义颜色。
调色板·amber / 11--color-amber-50、--color-amber-100、--color-amber-200、--color-amber-300、--color-amber-400、--color-amber-500、--color-amber-600、--color-amber-700、--color-amber-800、--color-amber-900、--color-amber-950两家共用的基础色阶;语义主题可以引用它们,也可以直接使用自定义颜色。
调色板·yellow / 11--color-yellow-50、--color-yellow-100、--color-yellow-200、--color-yellow-300、--color-yellow-400、--color-yellow-500、--color-yellow-600、--color-yellow-700、--color-yellow-800、--color-yellow-900、--color-yellow-950两家共用的基础色阶;语义主题可以引用它们,也可以直接使用自定义颜色。
调色板·lime / 11--color-lime-50、--color-lime-100、--color-lime-200、--color-lime-300、--color-lime-400、--color-lime-500、--color-lime-600、--color-lime-700、--color-lime-800、--color-lime-900、--color-lime-950两家共用的基础色阶;语义主题可以引用它们,也可以直接使用自定义颜色。
调色板·green / 11--color-green-50、--color-green-100、--color-green-200、--color-green-300、--color-green-400、--color-green-500、--color-green-600、--color-green-700、--color-green-800、--color-green-900、--color-green-950两家共用的基础色阶;语义主题可以引用它们,也可以直接使用自定义颜色。
调色板·emerald / 11--color-emerald-50、--color-emerald-100、--color-emerald-200、--color-emerald-300、--color-emerald-400、--color-emerald-500、--color-emerald-600、--color-emerald-700、--color-emerald-800、--color-emerald-900、--color-emerald-950两家共用的基础色阶;语义主题可以引用它们,也可以直接使用自定义颜色。
调色板·teal / 11--color-teal-50、--color-teal-100、--color-teal-200、--color-teal-300、--color-teal-400、--color-teal-500、--color-teal-600、--color-teal-700、--color-teal-800、--color-teal-900、--color-teal-950两家共用的基础色阶;语义主题可以引用它们,也可以直接使用自定义颜色。
调色板·cyan / 11--color-cyan-50、--color-cyan-100、--color-cyan-200、--color-cyan-300、--color-cyan-400、--color-cyan-500、--color-cyan-600、--color-cyan-700、--color-cyan-800、--color-cyan-900、--color-cyan-950两家共用的基础色阶;语义主题可以引用它们,也可以直接使用自定义颜色。
调色板·sky / 11--color-sky-50、--color-sky-100、--color-sky-200、--color-sky-300、--color-sky-400、--color-sky-500、--color-sky-600、--color-sky-700、--color-sky-800、--color-sky-900、--color-sky-950两家共用的基础色阶;语义主题可以引用它们,也可以直接使用自定义颜色。
调色板·blue / 11--color-blue-50、--color-blue-100、--color-blue-200、--color-blue-300、--color-blue-400、--color-blue-500、--color-blue-600、--color-blue-700、--color-blue-800、--color-blue-900、--color-blue-950两家共用的基础色阶;语义主题可以引用它们,也可以直接使用自定义颜色。
调色板·indigo / 11--color-indigo-50、--color-indigo-100、--color-indigo-200、--color-indigo-300、--color-indigo-400、--color-indigo-500、--color-indigo-600、--color-indigo-700、--color-indigo-800、--color-indigo-900、--color-indigo-950两家共用的基础色阶;语义主题可以引用它们,也可以直接使用自定义颜色。
调色板·violet / 11--color-violet-50、--color-violet-100、--color-violet-200、--color-violet-300、--color-violet-400、--color-violet-500、--color-violet-600、--color-violet-700、--color-violet-800、--color-violet-900、--color-violet-950两家共用的基础色阶;语义主题可以引用它们,也可以直接使用自定义颜色。
调色板·purple / 11--color-purple-50、--color-purple-100、--color-purple-200、--color-purple-300、--color-purple-400、--color-purple-500、--color-purple-600、--color-purple-700、--color-purple-800、--color-purple-900、--color-purple-950两家共用的基础色阶;语义主题可以引用它们,也可以直接使用自定义颜色。
调色板·fuchsia / 11--color-fuchsia-50、--color-fuchsia-100、--color-fuchsia-200、--color-fuchsia-300、--color-fuchsia-400、--color-fuchsia-500、--color-fuchsia-600、--color-fuchsia-700、--color-fuchsia-800、--color-fuchsia-900、--color-fuchsia-950两家共用的基础色阶;语义主题可以引用它们,也可以直接使用自定义颜色。
调色板·pink / 11--color-pink-50、--color-pink-100、--color-pink-200、--color-pink-300、--color-pink-400、--color-pink-500、--color-pink-600、--color-pink-700、--color-pink-800、--color-pink-900、--color-pink-950两家共用的基础色阶;语义主题可以引用它们,也可以直接使用自定义颜色。
调色板·rose / 11--color-rose-50、--color-rose-100、--color-rose-200、--color-rose-300、--color-rose-400、--color-rose-500、--color-rose-600、--color-rose-700、--color-rose-800、--color-rose-900、--color-rose-950两家共用的基础色阶;语义主题可以引用它们,也可以直接使用自定义颜色。
调色板·slate / 11--color-slate-50、--color-slate-100、--color-slate-200、--color-slate-300、--color-slate-400、--color-slate-500、--color-slate-600、--color-slate-700、--color-slate-800、--color-slate-900、--color-slate-950两家共用的基础色阶;语义主题可以引用它们,也可以直接使用自定义颜色。
调色板·gray / 11--color-gray-50、--color-gray-100、--color-gray-200、--color-gray-300、--color-gray-400、--color-gray-500、--color-gray-600、--color-gray-700、--color-gray-800、--color-gray-900、--color-gray-950两家共用的基础色阶;语义主题可以引用它们,也可以直接使用自定义颜色。
调色板·zinc / 11--color-zinc-50、--color-zinc-100、--color-zinc-200、--color-zinc-300、--color-zinc-400、--color-zinc-500、--color-zinc-600、--color-zinc-700、--color-zinc-800、--color-zinc-900、--color-zinc-950两家共用的基础色阶;语义主题可以引用它们,也可以直接使用自定义颜色。
调色板·neutral / 11--color-neutral-50、--color-neutral-100、--color-neutral-200、--color-neutral-300、--color-neutral-400、--color-neutral-500、--color-neutral-600、--color-neutral-700、--color-neutral-800、--color-neutral-900、--color-neutral-950两家共用的基础色阶;语义主题可以引用它们,也可以直接使用自定义颜色。
调色板·stone / 11--color-stone-50、--color-stone-100、--color-stone-200、--color-stone-300、--color-stone-400、--color-stone-500、--color-stone-600、--color-stone-700、--color-stone-800、--color-stone-900、--color-stone-950两家共用的基础色阶;语义主题可以引用它们,也可以直接使用自定义颜色。
调色板·mauve / 11--color-mauve-50、--color-mauve-100、--color-mauve-200、--color-mauve-300、--color-mauve-400、--color-mauve-500、--color-mauve-600、--color-mauve-700、--color-mauve-800、--color-mauve-900、--color-mauve-950两家共用的基础色阶;语义主题可以引用它们,也可以直接使用自定义颜色。
调色板·olive / 11--color-olive-50、--color-olive-100、--color-olive-200、--color-olive-300、--color-olive-400、--color-olive-500、--color-olive-600、--color-olive-700、--color-olive-800、--color-olive-900、--color-olive-950两家共用的基础色阶;语义主题可以引用它们,也可以直接使用自定义颜色。
调色板·mist / 11--color-mist-50、--color-mist-100、--color-mist-200、--color-mist-300、--color-mist-400、--color-mist-500、--color-mist-600、--color-mist-700、--color-mist-800、--color-mist-900、--color-mist-950两家共用的基础色阶;语义主题可以引用它们,也可以直接使用自定义颜色。
调色板·taupe / 11--color-taupe-50、--color-taupe-100、--color-taupe-200、--color-taupe-300、--color-taupe-400、--color-taupe-500、--color-taupe-600、--color-taupe-700、--color-taupe-800、--color-taupe-900、--color-taupe-950两家共用的基础色阶;语义主题可以引用它们,也可以直接使用自定义颜色。
调色板·黑白 / 2--color-black、--color-white基础纯黑与纯白;与 HeroUI 的基础色变量 --white / --black 分开。
字体族 / 3--font-sans、--font-serif、--font-mono两家均可使用;UI 与正文的字体角色仍由项目决定。
间距 / 1--spacing默认 0.25rem;p-4、gap-4 等按单位计算,并没有默认 spacing-4 变量。
响应式断点 / 5--breakpoint-sm、--breakpoint-md、--breakpoint-lg、--breakpoint-xl、--breakpoint-2xl默认 sm、md、lg、xl、2xl,定义响应式 variants。
容器尺度 / 13--container-3xs、--container-2xs、--container-xs、--container-sm、--container-md、--container-lg、--container-xl、--container-2xl、--container-3xl、--container-4xl、--container-5xl、--container-6xl、--container-7xl用于尺寸工具类与容器查询的命名尺度。
字号与配套行高·xs / 2--text-xs、--text-xs--line-height字号 0.75rem;配套行高 calc(1 / 0.75)。
字号与配套行高·sm / 2--text-sm、--text-sm--line-height字号 0.875rem;配套行高 calc(1.25 / 0.875)。
字号与配套行高·base / 2--text-base、--text-base--line-height字号 1rem;配套行高 calc(1.5 / 1)。
字号与配套行高·lg / 2--text-lg、--text-lg--line-height字号 1.125rem;配套行高 calc(1.75 / 1.125)。
字号与配套行高·xl / 2--text-xl、--text-xl--line-height字号 1.25rem;配套行高 calc(1.75 / 1.25)。
字号与配套行高·2xl / 2--text-2xl、--text-2xl--line-height字号 1.5rem;配套行高 calc(2 / 1.5)。
字号与配套行高·3xl / 2--text-3xl、--text-3xl--line-height字号 1.875rem;配套行高 calc(2.25 / 1.875)。
字号与配套行高·4xl / 2--text-4xl、--text-4xl--line-height字号 2.25rem;配套行高 calc(2.5 / 2.25)。
字号与配套行高·5xl / 2--text-5xl、--text-5xl--line-height字号 3rem;配套行高 1。
字号与配套行高·6xl / 2--text-6xl、--text-6xl--line-height字号 3.75rem;配套行高 1。
字号与配套行高·7xl / 2--text-7xl、--text-7xl--line-height字号 4.5rem;配套行高 1。
字号与配套行高·8xl / 2--text-8xl、--text-8xl--line-height字号 6rem;配套行高 1。
字号与配套行高·9xl / 2--text-9xl、--text-9xl--line-height字号 8rem;配套行高 1。
字重 / 9--font-weight-thin、--font-weight-extralight、--font-weight-light、--font-weight-normal、--font-weight-medium、--font-weight-semibold、--font-weight-bold、--font-weight-extrabold、--font-weight-black从 thin 到 black 的九档。
字距 / 6--tracking-tighter、--tracking-tight、--tracking-normal、--tracking-wide、--tracking-wider、--tracking-widest从 tighter 到 widest 的六档。
独立行高 / 5--leading-tight、--leading-snug、--leading-normal、--leading-relaxed、--leading-loose五档无单位行高;与字号配套的 line-height 分开。
圆角尺度 / 8--radius-xs、--radius-sm、--radius-md、--radius-lg、--radius-xl、--radius-2xl、--radius-3xl、--radius-4xlshadcn 覆写 sm 至 4xl;HeroUI 覆写 xs 至 4xl,最终公式看主表。
外阴影 / 7--shadow-2xs、--shadow-xs、--shadow-sm、--shadow-md、--shadow-lg、--shadow-xl、--shadow-2xlTailwind 的通用尺度;与 HeroUI 的 surface/overlay/field 语义阴影分开。
内阴影 / 3--inset-shadow-2xs、--inset-shadow-xs、--inset-shadow-sm两家可以复用的通用工具类尺度。
投影滤镜 / 6--drop-shadow-xs、--drop-shadow-sm、--drop-shadow-md、--drop-shadow-lg、--drop-shadow-xl、--drop-shadow-2xl与 box-shadow 作用机制不同。
文字阴影 / 5--text-shadow-2xs、--text-shadow-xs、--text-shadow-sm、--text-shadow-md、--text-shadow-lg两家可以复用的文字阴影尺度。
缓动 / 3--ease-in、--ease-out、--ease-in-out原生三条曲线;HeroUI 另加的 21 个具名项已列在主表。
动画 / 4--animate-spin、--animate-ping、--animate-pulse、--animate-bounce原生 spin、ping、pulse、bounce;与 HeroUI 额外动画分开。
模糊 / 7--blur-xs、--blur-sm、--blur-md、--blur-lg、--blur-xl、--blur-2xl、--blur-3xlxs 至 3xl 七档。
透视 / 5--perspective-dramatic、--perspective-near、--perspective-normal、--perspective-midrange、--perspective-distantdramatic 至 distant 五档。
宽高比 / 1--aspect-video默认 16 / 9。
默认过渡参数 / 2--default-transition-duration、--default-transition-timing-function默认 duration 为 150ms,另有默认 timing function。
默认字体配置 / 6--default-font-family、--default-font-feature-settings、--default-font-variation-settings、--default-mono-font-family、--default-mono-font-feature-settings、--default-mono-font-variation-settings默认文本与等宽字体的 family、feature settings、variation settings。被引用的可选字体设置不等于文件中另有声明。
Deprecated·兼容旧名 / 1--blur已弃用;该兼容名在主题文件中的值为 8px。
Deprecated·兼容旧名 / 1--shadow已弃用;该兼容名在主题文件中的值为 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)。
Deprecated·兼容旧名 / 1--shadow-inner已弃用;该兼容名在主题文件中的值为 inset 0 2px 4px 0 rgb(0 0 0 / 0.05)。
Deprecated·兼容旧名 / 1--drop-shadow已弃用;该兼容名在主题文件中的值为 0 1px 2px rgb(0 0 0 / 0.1), 0 1px 1px rgb(0 0 0 / 0.06)。
Deprecated·兼容旧名 / 1--radius已弃用;该兼容名在主题文件中的值为 0.25rem。
Deprecated·兼容旧名 / 1--max-width-prose已弃用;该兼容名在主题文件中的值为 65ch。

例如 Tailwind 的 Deprecated --radius 是 0.25rem,但两家都有同名的普通根圆角变量;应以项目实际层叠后的值为准,不能把兼容值当成两家组件的最终圆角。编译器生成的 --tw-shadow、--tw-ring-shadow 等组合样式变量也不属于 theme.css 的默认主题清单。

Theme variables define which design values are exposed to Tailwind utilities. They are distinct from the internal custom properties that utilities use to compose CSS. Tailwind Theme Variables、4.3.3 默认主题源码

一个最小扩展例子:库负责组件颜色,项目负责页面规则

下面可加入一个已经配置 HeroUI v3 与 Tailwind v4 的 React 项目。它演示主题 token 也能服务自建 HTML,以及项目如何补充内容宽度和 section 间距;不是完整的项目脚手架。

app.css:

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:

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,一个网站还要设计什么

信息架构、字体、布局、内容、交互恢复与交付流程另见网站 UI 设计实践:从内容结构到交互验收。该文包含设计方法、案例、验收条件与资料选读路线;这里集中比较两家的 token 与组件样式机制。

参考资料

博客助手

正在打开博客助手…