第25课:CSS 变量(自定义属性)——--*、var()、作用域与暗色模式
CSS 变量,正式名称为自定义属性(Custom Properties),是 CSS 原生的变量系统。与预处理器(Sass、Less)的变量不同,CSS 变量在浏览器中动态生效,可以响应级联、继承、媒体查询和 JavaScript 操作。它们是实现主题切换(暗色模式)、设计令牌(Design Tokens)和组件级复用的核心工具。本节课将深入讲解自定义属性的声明、使用、作用域规则,以及如何利用它们构建动态主题系统。
1. 自定义属性的声明与使用
自定义属性以 -- 开头,后跟属性名。它必须在一个 CSS 规则块内声明,且区分大小写。
1 | :root { |
--primary-color 就是一个自定义属性,它存储了一个颜色值。使用时,通过 var() 函数引用。
1 | .button { |
1.1 var() 函数的语法
1 | var(<custom-property-name>, <fallback-value>?) |
- 第一个参数:自定义属性名称(必须包含
--前缀)。 - 第二个参数(可选):回退值。当自定义属性未定义,或浏览器不支持自定义属性时,使用该回退值。
1 | .button { |
注意:var() 的回退值仅用于属性值缺失的情况,若属性值无效(如 --primary-color: 10px 被用作 color 值),浏览器会使用属性的继承值或初始值,而非回退值。
1.2 自定义属性的值类型
自定义属性可以存储任何有效的 CSS 值——颜色、尺寸、字符串、完整声明片段,甚至另一个变量的引用。
1 | :root { |
2. 作用域与继承
2.1 自定义属性遵循 CSS 级联规则
自定义属性也是 CSS 属性,同样遵循优先级、继承和级联规则。在 :root 中声明的属性是全局的(被所有元素继承);在某个选择器中声明的属性仅对该选择器及其后代有效。
1 | <div class="parent"> |
1 | .parent { |
2.2 组件级作用域
可以在组件选择器中重新定义变量,覆盖全局值,从而创建组件级样式变体。
1 | :root { |
1 | <button class="btn">默认</button> |
每个变体类仅重新定义变量,而不修改具体样式属性,保持了样式的单一来源和高度复用。
2.3 动态作用域与媒体查询
自定义属性可在 @media 内部重新定义,实现响应式样式。
1 | :root { |
3. JavaScript 操作自定义属性
CSS 自定义属性可以通过 JavaScript 动态读写,这是实现主题切换、用户偏好的核心能力。
3.1 getPropertyValue 和 setProperty
1 | // 读取根元素的自定义属性 |
3.2 批量更新与性能
直接操作 style 属性会触发元素的重绘。若需频繁更新多个变量(如跟随鼠标移动的渐变),建议使用 requestAnimationFrame 合并更新。
1 | document.addEventListener('mousemove', (e) => { |
4. 暗色模式:使用 CSS 变量 + 媒体查询实现主题切换
借助 prefers-color-scheme 媒体查询和自定义属性,可以构建自动跟随系统主题的暗色模式。
4.1 定义明暗主题的变量
1 | :root { |
4.2 在样式中使用变量
1 | body { |
这样,当用户在操作系统层面切换明暗模式时,页面会自动切换主题。
4.3 手动切换 + 系统偏好
为提供更好的用户体验,通常允许用户手动切换主题,并持久化选择。JavaScript 可以将选择存储到 localStorage,并动态在 :root 上设置 data-theme 属性。
1 | const toggleBtn = document.getElementById('theme-toggle'); |
CSS 需配合 data-theme 属性:
1 | :root { |
5. 自定义属性的进阶用法
5.1 与 calc() 结合动态计算
1 | :root { |
5.2 在动画中应用自定义属性
自定义属性的值可以随时间变化(通过 JavaScript 定时更新),但这不会自动产生动画过渡。如果需要动画,可以使用 @property(CSS Houdini)定义自定义属性的类型,使其可被动画化。
1 | @property --gradient-angle { |
注意:@property 目前在现代浏览器中支持良好(Chrome 85+、Edge 85+、Safari 15.4+、Firefox 128+)。
课后练习
一、概念自测(选择题 / 填空题)
(单选) CSS 自定义属性名称必须以什么开头?
A.$
B.@
C.--
D.var-(单选) 在
var()函数中,第二个参数的作用是?
A. 设置变量的最小值。
B. 当自定义属性未定义时使用的回退值。
C. 当自定义属性值无效时使用的回退值。
D. 声明该变量是否可继承。(填空) 要通过 JavaScript 设置根元素的自定义属性
--accent-color为red,应使用document.documentElement.style.setProperty('______', 'red')。(多选) 关于 CSS 自定义属性的说法,哪些是正确的?
A. 它们遵循 CSS 级联和继承规则。
B. 它们可以在@media查询中重新定义。
C. 它们只能在:root中声明。
D. 它们的值可以包含另一个var()引用。
二、AI 编程任务:编写面向 AI 的提示词
场景:你需要实现一个使用 CSS 自定义属性的按钮组件库,包含三种变体(默认、主要、危险),并支持暗色模式自动切换(跟随系统 prefers-color-scheme)。要求如下:
- 定义全局变量:
--btn-bg、--btn-color、--btn-radius,默认值分别为#e0e0e0、#333、6px。 - 三种变体仅需覆盖
--btn-bg和--btn-color,不修改其他属性。 - 暗色模式下,
--btn-bg自动调整为深色值。 - 提供一套简洁的 HTML 示例展示三种按钮。
- 使用原生 CSS,无需预处理器。
任务要求:请写出一段完整的中文提示词,发送给 AI,使其生成符合上述要求的完整 HTML + CSS 代码。提示词中需明确指定自定义属性的命名、组件变体的实现策略、暗色模式的媒体查询写法。
三、Agent 模式下的提示词示例
在现代 AI 编程 Agent(如 Codex、Claude Code、OpenCode)中,你无需逐行描述 CSS,只需以架构和约束的方式描述目标,Agent 会自行生成文件、管理变量和选择器。
Agent 提示词:
你是一个资深前端开发 Agent,使用 CSS 自定义属性实现一个按钮组件库和暗色模式主题系统。请完成以下任务:
- 在
styles/theme.css中定义全局设计令牌(Design Tokens):
- 明色主题:
--color-bg: #fff、--color-text: #1a1a1a、--color-primary: #3f51b5、--color-danger: #e74c3c- 暗色主题:使用
prefers-color-scheme: dark覆盖上述变量为暗色值。- 在
styles/button.css中定义.btn基础样式,使用var()引用令牌。添加.btn-primary和.btn-danger变体类,仅覆盖相关变量。- 在
index.html中展示三个按钮,并引入 CSS 文件。- 额外要求:在页面右上角添加一个“切换主题”按钮,通过 JS 在
<html>上设置data-theme属性,手动切换明暗主题。JS 逻辑放在scripts/theme-toggle.js。- 所有文件需遵循现代 CSS 最佳实践,代码整洁,适当注释。
课后练习答案
一、概念自测答案
C
- 解析:CSS 自定义属性必须以
--开头,例如--main-color。
- 解析:CSS 自定义属性必须以
B
- 解析:
var()的第二个参数是回退值,当自定义属性未定义时使用。注意区分“未定义”和“值无效”:值无效时回退值不起作用。
- 解析:
--accent-color- 解析:
setProperty第一个参数需要完整的自定义属性名,包括--前缀。
- 解析:
A、B、D
- 解析:A 正确,自定义属性遵循级联和继承;B 正确,可在媒体查询内重新定义;C 错误,可以在任何选择器中声明;D 正确,
var()可以嵌套引用。
- 解析:A 正确,自定义属性遵循级联和继承;B 正确,可在媒体查询内重新定义;C 错误,可以在任何选择器中声明;D 正确,
二、AI 编程任务参考答案(提示词示例)
示例提示词:
“请使用 CSS 自定义属性创建一个按钮系统。要求:
- 在
:root中定义--btn-bg: #e0e0e0、--btn-color: #333、--btn-radius: 6px、--btn-padding: 8px 16px。.btn基础类使用这些变量。.btn-primary覆盖--btn-bg: #3f51b5; --btn-color: #fff;.btn-danger覆盖--btn-bg: #e74c3c; --btn-color: #fff;- 添加
@media (prefers-color-scheme: dark)将默认--btn-bg改为#555,--btn-color改为#eee。- HTML 提供三个按钮:默认、主要、危险。
- 代码注释清晰,直接输出完整 HTML 文件。”
三、Agent 模式下的提示词示例
(见上文 Agent 提示词部分)