B06 自定义属性与主题
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 验证与讨论
- 打开页面,确认浅色主题下正常显示。
- 在控制台执行
document.documentElement.dataset.theme = 'dark',确认整页切换。 - 删除
--text-muted的定义,观察回退值是否生效(未写回退时文字颜色回到继承值)。 - 系统切换深色模式,确认在未强制主题时跟随系统。
| 检查项 | 预期结果 | 说明 |
|---|---|---|
| 主题切换 | 背景与文字同时变化 | 语义层赋值 |
| 组件无改动 | 切换后组件样式不变 | 只读语义变量 |
| 回退生效 | 缺失变量时使用回退值 | 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 | 暗色主题需重新校验对比度 | 讨论 |
- 上一篇:B05-变换过渡与动画
- 下一篇:B07-背景渐变与滤镜
- 返回:B00-总结 | 科目 README
参考文献
[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
还没有评论,快来抢沙发吧~