颜色
以自适应角色构建界面颜色,结合圆角与阴影突出表面,并选择明暗模式。
本页内容
#概览
以角色构建界面颜色:表面、线条与文本的 class 在明暗模式中保持不变。当颜色本身就是重点,例如插图或品牌点缀时,再选用色阶。颜色变化应对应角色、状态,或读者需要辨识的差异。
模式改变,角色不变
只需选择一次角色。它的 light-dark() 值会跟随元素配色方案,包括嵌在另一配色中的区域。
文字说明
- 保留角色. 两个区域使用相同的表面、分隔线、强调文本与次要文本 class。
- 按区域解析. 角色值使用 light-dark()。每个区域的 color-scheme 会选择亮色或暗色,无需改变 class。
#选择角色或色阶
| 需求 | 使用 | 示例 |
|---|---|---|
| 页面结构与文本层级 | 颜色角色 | bg-surface-raised, b-line-divider, fg-text-muted |
| 特定颜色 | 色阶 | bg-blue-5, fg-blue-60 |
同一个布局,两种模式
<section class="p-md r-lg b(1) b-solid b-line-divider bg-surface-raised fg-text-body"> <h2 class="m(0) text-lg font-regular fg-text-strong">Project updates</h2> <p class="mt-xs mb-md text-sm fg-text-muted">Three milestones changed this week.</p> <a class="fg-text-link underline" href="/updates">Continue</a></section>生成的 CSS
@layer theme { :root, :host { --color-zinc-40: oklch(68.6% .0028 265); --color-zinc-60: oklch(48.6% .003 265); --color-zinc-90: oklch(24.6% .0022 265) } :root, :host { --color-surface-raised: light-dark(var(--color-white), var(--color-zinc-90)); --color-line-divider: light-dark(oklch(0% 0 none / .12), oklch(100% 0 none / .12)); --color-text-muted: light-dark(var(--color-zinc-60), var(--color-zinc-40)) } :root, :host { --color-white: oklch(100% 0 none) }}@layer utilities { .bg-surface-raised { background-color: var(--color-surface-raised) } .b-line-divider { border-color: var(--color-line-divider) } .fg-text-muted { color: var(--color-text-muted) }}角色是颜色键名的一部分:bg-surface-raised 读取 --color-surface-raised,b-line-divider 读取 --color-line-divider。线条角色只设置颜色,因此边框宽度与样式须另外指定。颜色角色列出所有角色,调色板则列出完整色阶。
边框颜色需要宽度与样式配合
所有方框初始均为 border-style: none。示例的 border-box 尺寸相同,仅改变所列边框声明。
文字说明
- b-line-divider 提供 border-color,b(2) 提供 border-width。两者均不改变 none 的边框样式。
- 加入 b-solid 后才会绘制 2px 边框。边框可沿用组件中其他已编写的必要条件,因此仅看到颜色类时请检查完整样式。
区分固定色阶与语义角色
特定颜色使用色阶;需要随场景调整的决策则使用角色。
文字说明
两条路径分别呈现固定色阶,以及选择明暗值的角色。
#固定与自适应色相别名
fg-yellow 这类色相别名是固定颜色,在每个模式中都使用相同的值。fg-text-yellow 这类文本色相别名则会按模式选择适合文本的前景色阶。请在实际背景上检查对比度;自适应别名无法保证每种搭配都合适。
<svg class="fg-yellow" fill="currentColor" aria-hidden="true">…</svg><p class="fg-text-yellow">Pending review</p>生成的 CSS
@layer theme { :root, :host { --color-yellow-40: oklch(91.5% .1909 101.1); --color-yellow-90: oklch(52.17% .1199 62.2) } :root, :host { --color-yellow: var(--color-yellow-40); --color-text-yellow: light-dark(var(--color-yellow-90), var(--color-yellow-40)) }}@layer utilities { .fg-text-yellow { color: var(--color-text-yellow) } .fg-yellow { color: var(--color-yellow) }}彩色标签使用文本色相别名;正文、强调文本与链接则使用文本角色。
#凸起表面
凸起表面结合表面角色、圆角 token 与阴影 token。可交互的表面在悬停或获得焦点时,将阴影提高一级。普通卡片与控件使用小圆角;pill 留给刻意完全圆润的形状,例如下方徽章。
凸起表面
<a class="block p-md r-lg bg-surface-raised fg-text-body shadow-sm shadow-md:hover shadow-md:focus-visible" href="/notes"> <span class="flex items-center justify-between gap-sm"> <span class="fg-text-strong">Field notes</span> <span class="px-xs py-3xs pill bg-line-subtle text-xs fg-text-muted">12 new</span> </span></a>生成的 CSS
@layer theme { :root, :host { --radius-lg: .5rem } :root, :host { --shadow-sm: 0 0 0 1px light-dark(oklch(0% 0 none / .04), oklch(100% 0 none / .05)), 0 1px 1px -.5px light-dark(oklch(0% 0 none / .045), oklch(0% 0 none / .3)), 0 2px 2px -1px light-dark(oklch(0% 0 none / .04), oklch(0% 0 none / .26)), 0 4px 4px -2px light-dark(oklch(0% 0 none / .04), oklch(0% 0 none / .24)); --shadow-md: 0 0 0 1px light-dark(oklch(0% 0 none / .05), oklch(100% 0 none / .06)), 0 1px 1px -.5px light-dark(oklch(0% 0 none / .045), oklch(0% 0 none / .3)), 0 2px 2px -1px light-dark(oklch(0% 0 none / .04), oklch(0% 0 none / .26)), 0 4px 4px -2px light-dark(oklch(0% 0 none / .04), oklch(0% 0 none / .24)), 0 8px 8px -4px light-dark(oklch(0% 0 none / .045), oklch(0% 0 none / .26)) }}@layer utilities { .r-lg { border-radius: var(--radius-lg) } .pill { border-radius: calc(infinity * 1px) } .shadow-sm { box-shadow: var(--shadow-sm) } .shadow-md\:hover:hover { box-shadow: var(--shadow-md) }}预设阴影用 light-dark() 设置颜色,因此 shadow-sm 保留几何形状,并跟随元素的配色方案。pill 是固定工具类,不是圆角 token;宽高相等时会形成圆形。使用 rtl-lg、rtr-lg、rbl-lg 与 rbr-lg 可分别设置单个角。各种尺度与自定义方式请见圆角与阴影。
相同 pill 圆角随方框形状调整
高度保持 64px。pill 提供圆角半径,示例尺寸来自独立尺寸声明。
文字说明
- pill 设置全圆角半径,不设置宽度、高度或内边距。
- 宽高相等时形成圆形。同高但较宽的方框形成胶囊,因此仅靠类并不保证圆形。
#明暗模式
预设在根元素设置 color-scheme: light dark,角色与阴影则使用 light-dark(),因此页面无需额外 class 就会跟随系统偏好。若要指定模式,在根元素、嵌套区域或 Shadow Host 上设置 data-theme="light" 或 data-theme="dark":
一个 token,两种局部配色
@theme { --color-surface-notes: light-dark(#f4f4f5, #27272a);}@layer base { [data-theme="light"] { color-scheme: light; } [data-theme="dark"] { color-scheme: dark; }}<section data-theme="dark" class="p-md r-lg bg-surface-notes fg-text-body wrap-anywhere"> <h2 class="m(0) text-lg font-regular">Dark notes</h2> <p class="mt-xs mb-md text-sm">This region chooses dark.</p> <article data-theme="light" class="p-md r-md bg-surface-notes fg-text-body"> <h3 class="m(0) text-md font-regular">A light region inside</h3> <p class="mt-xs mb(0) text-sm">The same surface token follows this article’s light scheme.</p> </article></section>生成的 CSS
@layer base { [data-theme="light"] { color-scheme: light; } [data-theme="dark"] { color-scheme: dark; }}@layer theme { :root, :host { --color-zinc-30: oklch(78% .0025 265); --color-zinc-70: oklch(39.8% .0028 265) } :root, :host { --color-text-body: light-dark(var(--color-zinc-70), var(--color-zinc-30)) } :root, :host { --font-weight-regular: 400; --font-size-sm: .875rem; --font-size-md: 1rem; --font-size-lg: 1.125rem; --text-sm: var(--font-size-sm); --text-sm--line-height: 1.5; --text-sm--letter-spacing: 0; --text-md: var(--font-size-md); --text-md--line-height: 1.6; --text-md--letter-spacing: 0; --text-lg: var(--font-size-lg); --text-lg--line-height: 1.5; --text-lg--letter-spacing: 0; --radius-md: .375rem; --radius-lg: .5rem; --spacing-xs: .5rem; --spacing-md: 1rem } :root, :host { --color-surface-notes: light-dark(#f4f4f5, #27272a) }}@layer utilities { .text-lg { font-size: var(--text-lg); line-height: var(--text-lg--line-height, normal); letter-spacing: var(--text-lg--letter-spacing, normal) } .text-md { font-size: var(--text-md); line-height: var(--text-md--line-height, normal); letter-spacing: var(--text-md--letter-spacing, normal) } .text-sm { font-size: var(--text-sm); line-height: var(--text-sm--line-height, normal); letter-spacing: var(--text-sm--letter-spacing, normal) } .bg-surface-notes { background-color: var(--color-surface-notes) } .r-lg { border-radius: var(--radius-lg) } .r-md { border-radius: var(--radius-md) } .fg-text-body { color: var(--color-text-body) } .font-regular { font-weight: var(--font-weight-regular) } .m\(0\) { margin: calc(0 * 1px) } .mb-md { margin-bottom: var(--spacing-md) } .mt-xs { margin-top: var(--spacing-xs) } .mb\(0\) { margin-bottom: calc(0 * 1px) } .wrap-anywhere { overflow-wrap: anywhere } .p-md { padding: var(--spacing-md) }}预设已内置这两个 data-theme 选择器;示例 CSS 再次列出它们,以说明启用方式。用 light-dark() 定义的自定义颜色也会以相同方式调整,切换模式无需重新编译。嵌套区域会继承父级 token,除非自行重新声明;品牌主题应使用自己的选择器,例如 [data-brand="ocean"],并在其中重新声明角色 token。
@light 与 @dark 后缀是 prefers-color-scheme 媒体查询。手动设置 data-theme 会改变 token,但不会启用这些查询。若 class 必须响应手动选择,请声明选择器 mixin 并应用:
@mixin --manual-dark { &:where([data-theme="dark"], [data-theme="dark"] *) { @contents; }}<img class="(opacity:.85)@apply(--manual-dark)" src="/chart.png" alt="Weekly usage" />这个 mixin 检查的是属性,而不是实际使用的配色方案,因此在嵌套的亮色区域内也会匹配。优先使用自适应 token;作用范围与解析方式请见变量与模式。
祖先选择器可以进入浅色区域
虚线路径追踪显式 dark 属性。内层浅色边界改变 color-scheme,不改变选择器检查的祖先关系。
文字说明
- 本文的 --manual-dark 混入匹配 [data-theme="dark"] 本身或其后代元素。
- 嵌套 data-theme="light" 区域中的图表仍是外层 dark 区域的后代。因此即使 light-dark() 值使用本地浅色配色,其不透明度包装仍然匹配。
- 需要跟随实际采用配色的值请使用自适应令牌;属性选择器仅检查其显式指定的关系。
最近的配色方案决定颜色
嵌套边界代表 color-scheme。Token 名称与标记保持不变。
文字说明
亮色文档包住一个暗色区域。同一个语义 token 在外侧绘制亮色表面,在区域内绘制暗色表面。
#在实际场景中检查角色
在两种配色中检查相同内容。确认强调与次要文本和实际表面的对比度、链接的普通与焦点状态,以及用来识别控件的边界。颜色 token 提供一个值,本身不会建立对比度、确保焦点可见,或赋予状态含义。
选中项或状态应搭配标签、图标或其他可见提示,避免只靠颜色。嵌套区域须单独验证:深色页面中的手动亮色区域,仍需有适当且易读的表面与前景搭配。











