B06 自定义属性与主题

2026-10-31 17:140 阅读约 10 分钟CSS

B06 自定义属性与主题

摘要:自定义属性(CSS 变量)是可继承、可运行时修改、可带类型的值。把颜色、间距、圆角收敛成两级变量——基础值与应用语义——之后,换主题只需改一组赋值。本文给出两级令牌的组织方式、暗色模式的切换写法,以及三条常见陷阱。
关键词:自定义属性,设计令牌,主题,继承,回退

所属:frontend/css / B-进阶 | 上一篇:B05-变换过渡与动画 | 下一篇:B07-背景渐变与滤镜 | 返回:B00-总结

1 引言

硬编码的颜色值会让主题切换变成全文件替换。自定义属性把「用哪个颜色」与「颜色是什么」分开:组件只引用语义变量,具体值按主题赋值。

本文给出两级令牌与切换写法。范围见表 1-1。

项 本文覆盖 本文不覆盖
定义 --name: value、作用域、继承 注册属性(@property)细节
令牌 基础值与应用语义两级 设计系统完整规范
主题 data-theme 切换、系统偏好跟随 主题持久化与服务端渲染

2 预备知识

项 要求 核对方式
环境 本地服务器可用 浏览器能打开 localhost
文件 css-practice/theme.html 编码 UTF-8
浏览器 Chrome 130+,Computed 面板 能看到变量计算值
前置 B01 理解继承与层叠

3 原理与实现

3.1 两级令牌

组件只读语义层,语义层指向基础层;换主题只改语义层的赋值,如图 3-1 所示。

flowchart LR
    A["基础令牌<br/>--gray-900: #0f172a"] --> B["语义令牌<br/>--text-strong: var(--gray-900)"]
    B --> C["组件<br/>color: var(--text-strong)"]
    D["[data-theme='dark']<br/>--text-strong: #e2e8f0"] --> B

图 3-1 基础令牌与语义令牌的关系

图上可见:组件不直接引用基础令牌,否则切换主题时需要改动组件;语义层是对外契约。

3.2 定义与切换

/* 运行环境:Chrome 130+;css-practice/theme.css */
:root {
  /* 基础层 */
  --gray-0: #ffffff;
  --gray-50: #f8fafc;
  --gray-900: #0f172a;
  --blue-600: #1d4ed8;

  /* 语义层 */
  --surface: var(--gray-0);
  --surface-alt: var(--gray-50);
  --text-strong: var(--gray-900);
  --text-muted: #475569;
  --brand: var(--blue-600);
  --radius: 0.75rem;
}

[data-theme="dark"] {
  --surface: #0b1220;
  --surface-alt: #131c2e;
  --text-strong: #e2e8f0;
  --text-muted: #94a3b8;
  --brand: #60a5fa;
}

body { background: var(--surface); color: var(--text-strong); }

.card {
  background: var(--surface-alt);
  border-radius: var(--radius);
  color: var(--text-strong);
  padding: 1.25rem;
}

.card__meta { color: var(--text-muted); }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --surface: #0b1220;
    --surface-alt: #131c2e;
    --text-strong: #e2e8f0;
    --text-muted: #94a3b8;
    --brand: #60a5fa;
  }
}

变量的使用规则见表 3-1。

规则 说明 反例
组件只用语义变量 改主题不改组件 组件里写 var(--gray-900)
变量名表达用途 --text-muted 而非 --color-2 用序号命名
提供回退值 var(--brand, #1d4ed8) 变量缺失导致整条声明失效
作用域就近 局部变量定义在组件选择器上 全部堆在 :root
不用于高频动画属性 修改变量会触发重算 每帧改变量驱动动画

令牌层级的维护规则见表 3-2。

层级 内容 改动频率
基础 色阶、字号、间距刻度 极少
语义 背景、正文、边框、品牌色 换主题时改
组件 卡片内边距、按钮高度 组件迭代时改

4 验证与讨论

  1. 打开页面,确认浅色主题下正常显示。
  2. 在控制台执行 document.documentElement.dataset.theme = 'dark',确认整页切换。
  3. 删除 --text-muted 的定义,观察回退值是否生效(未写回退时文字颜色回到继承值)。
  4. 系统切换深色模式,确认在未强制主题时跟随系统。
检查项 预期结果 说明
主题切换 背景与文字同时变化 语义层赋值
组件无改动 切换后组件样式不变 只读语义变量
回退生效 缺失变量时使用回退值 var(--x, fallback)
跟随系统 未强制主题时随系统 prefers-color-scheme

讨论与局限。 自定义属性默认按字符串处理,参与计算时需要类型正确,否则 calc() 会失败;需要类型约束时使用 @property 注册。另暗色模式不是简单反色,对比度需重新核对(C04)。

5 常见问题

现象 原因 处理
变量不生效 定义在未匹配的选择器上 确认作用域覆盖目标元素
切换主题无效 组件里写死颜色值 全部替换为 var()
变量在 calc 中报错 值带单位且拼接方式错误 使用 calc(var(--gap) * 2)
暗色下文字看不清 只改了背景未改文字 同步调整语义层文字色
首屏闪白 主题由脚本在加载后设置 在 HTML 上内联主题属性

6 结论

序号 结论 依据
1 组件只引用语义令牌 图 3-1
2 变量名表达用途,不表达序号 表 3-1
3 使用处提供回退值 表 3-1
4 暗色主题需重新校验对比度 讨论

参考文献

[1] MDN Web Docs · 使用 CSS 自定义属性,https://developer.mozilla.org/zh-CN/docs/Web/CSS/Using_CSS_custom_properties(访问于 2026-10-31)
[2] CSS Custom Properties for Cascading Variables Module Level 1,W3C,https://www.w3.org/TR/css-variables-1/(访问于 2026-10-31)

返回文章列表

评论 0

0/2000

还没有评论,快来抢沙发吧~