当微软宣布 Edge 将支持 CSS 变量时,这项能力在主要浏览器里已经齐了。原生 CSS 从此可以声明可复用的自定义属性,再用 var() 读出来。变量名用两根连词线 --,因为 $foo 被 Sass 占用,@foo 被 Less 占用。
CSS 变量又叫 CSS 自定义属性(CSS custom properties):和 color、font-size 一样写在规则里,只是没有默认含义。变量名大小写敏感,--header-color 和 --Header-Color 是两个变量。
一、变量的声明
声明时变量名前面加两根连词线:
body {
--foo: #7F583F;
--bar: #F7EFD2;
}
上面在 body 里声明了 --foo 和 --bar。各种值都可以放进 CSS 变量:
:root{
--main-color: #4d4e53;
--main-bg: rgb(255, 255, 255);
--logo-border-color: rebeccapurple;
--header-height: 68px;
--content-padding: 10px 20px;
--base-line-height: 1.428571429;
--transition-duration: .35s;
--external-link: "external link";
--margin-top: calc(2vh + 20px);
}
二、var() 函数
var() 用于读取变量:
a {
color: var(--foo);
text-decoration-color: var(--bar);
}
第二个参数是默认值,变量不存在时使用:
color: var(--foo, #7F583F);
第二个参数不处理内部的逗号或空格,都视作参数的一部分:
var(--font-stack, "Roboto", "Helvetica");
var(--pad, 10px 15px 20px);
var() 也可以用在变量的声明里:
:root {
--primary-color: red;
--logo-text: var(--primary-color);
}
注意:变量值只能当属性值,不能当属性名:
.foo { --side: margin-top; /* 无效 */ var(--side): 20px;}
| 写法 |
含义 |
--foo: #7F583F; |
声明自定义属性 |
var(--foo) |
读取 |
var(--foo, #7F583F) |
读取,缺省用第二参 |
var(--side): 20px |
当属性名,无效 |
三、变量值的类型
如果变量值是字符串,可以和其它字符串拼接:
--bar: 'hello';
--foo: var(--bar)' world';
利用这一点可以 debug:
body:after {
content: '--screen-category : 'var(--screen-category);
}
如果变量值是数值,不能和单位直接连用:
.foo {
--gap: 20;
/* 无效 */
margin-top: var(--gap)px;
}
必须用 calc() 把它们连起来:
.foo {
--gap: 20;
margin-top: calc(var(--gap) * 1px);
}
如果变量值带单位,就不能写成字符串:
/* 无效 */.foo { --foo: '20px'; font-size: var(--foo);}
/* 有效 */.foo { --foo: 20px; font-size: var(--foo);}
四、作用域
同一个 CSS 变量可以在多个选择器里声明。读取时优先级最高的声明生效,这和 CSS 的层叠(cascade)一致。下面这段里三段文字颜色不同:
<style>
:root { --color: blue; }
div { --color: green; }
#alert { --color: red; }
* { color: var(--color); }
</style>
<p>蓝色</p>
<div>绿色</div>
<div id="alert">红色</div>
变量的作用域就是它所在选择器的有效范围:
body {
--foo: #7F583F;
}
.content {
--bar: #F7EFD2;
}
--foo 的作用域是 body,--bar 的作用域是 .content。全局变量通常放 :root:
:root {
--main-color: #06c;
}
五、响应式布局
CSS 是动态的。可在 media 里改变量,让不同屏幕宽度用不同值:
body {
--primary: #7F583F;
--secondary: #F7EFD2;
}
a {
color: var(--primary);
text-decoration-color: var(--secondary);
}
@media screen and (min-width: 768px) {
body {
--primary: #F7EFD2;
--secondary: #7F583F;
}
}
六、兼容性处理
不支持 CSS 变量的浏览器,可以先写回退值:
a {
color: #7F583F;
color: var(--primary);
}
也可以用 @supports 检测:
@supports ( (--a: 0)) {
/* supported */
}
@supports ( not (--a: 0)) {
/* not supported */
}
七、JavaScript 操作
JS 也能检测是否支持 CSS 变量:
const isSupported = window.CSS && window.CSS.supports && window.CSS.supports('--a', 0);if (isSupported) { /* supported */} else { /* not supported */}
读写、删除的写法:
// 设置变量
document.body.style.setProperty('--primary', '#7F583F');
// 读取变量
document.body.style.getPropertyValue('--primary').trim();
// '#7F583F'
// 删除变量
document.body.style.removeProperty('--primary');
这意味着 JS 可以把任意值存进样式表。监听鼠标就是把坐标写入变量:
const docStyle = document.documentElement.style;
document.addEventListener('mousemove', (e) => {
docStyle.setProperty('--mouse-x', e.clientX);
docStyle.setProperty('--mouse-y', e.clientY);
});
对 CSS 无用的信息也可以放进变量,供 JS 读取:
--foo: if(x > 5) this.width = 10;
上面 --foo 在 CSS 里是无效语句,但 JS 读得到。可以把样式设定写在 CSS 变量里,让 JS 来读。CSS 变量因此是 JS 与 CSS 通信的一条路。
八、浏览器支持
当时全球网站流量约 77% 支持 CSS 变量,美国接近 90%。Scrimba.com 已经在用,因为客户大多使用现代浏览器。


一句话总结:用 –name 声明自定义属性,用 var() 读取(可带默认值);作用域跟选择器走,全局放 :root,media 里可改值,JS 用 setProperty / getPropertyValue 通信。