最后更新:2026-03-20
CSS 自定义属性(也叫 CSS 变量)支持已经很久了,但我发现不少人还在用 SCSS 变量或者把颜色值写死在每个地方。
其实 CSS 原生变量比 SCSS 变量能做更多事,最典型的就是运行时动态修改——这是预处理器变量做不到的。
基本语法
定义变量用 -- 开头,使用时用 var() 函数:
1 2 3 4 5 6 7 8 9 10 11 12 13
| :root { --color-primary: #3b82f6; --color-text: #1f2937; --spacing-base: 8px; --border-radius: 6px; }
.button { background-color: var(--color-primary); color: white; padding: calc(var(--spacing-base) * 1.5) calc(var(--spacing-base) * 3); border-radius: var(--border-radius); }
|
var() 还支持默认值,当变量未定义时回退:
1 2 3 4 5 6
| .card { background: var(--card-bg, white); color: var(--card-text, var(--color-text, #333)); }
|
CSS 变量和 SCSS 变量的区别
这是个容易混淆的点,两者看起来都是「变量」,但性质不同:
SCSS 变量:编译时替换,最终输出的 CSS 里没有变量,只有固定值。
1 2 3 4
| $primary: #3b82f6;
.button { background-color: $primary; }
|
CSS 变量:运行时存在,可以被 JavaScript 读写,可以在某个元素的作用域内覆盖。
1 2 3 4 5 6 7 8
| :root { --primary: #3b82f6; }
.dark-section { --primary: #93c5fd; }
.button { background-color: var(--primary); }
|
在 .dark-section 里面的 .button 会用 #93c5fd,外面的用 #3b82f6。这种作用域覆盖是 SCSS 做不到的。
实现主题切换
这是 CSS 变量最常见的应用场景,也是我项目里真正在用的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| :root { --bg-primary: #ffffff; --bg-secondary: #f3f4f6; --text-primary: #111827; --text-secondary: #6b7280; --border-color: #e5e7eb; --color-brand: #3b82f6; }
[data-theme='dark'] { --bg-primary: #111827; --bg-secondary: #1f2937; --text-primary: #f9fafb; --text-secondary: #9ca3af; --border-color: #374151; --color-brand: #60a5fa; }
.card { background: var(--bg-primary); color: var(--text-primary); border: 1px solid var(--border-color); }
|
用 JavaScript 切换主题只需要一行:
1 2 3 4 5 6 7
| document.documentElement.setAttribute('data-theme', 'dark')
document.documentElement.removeAttribute('data-theme')
document.documentElement.setAttribute('data-theme', 'light')
|
配合 localStorage 持久化:
1 2 3 4 5 6 7 8 9 10 11 12
| const THEME_KEY = 'preferred-theme'
function setTheme(theme) { document.documentElement.setAttribute('data-theme', theme) localStorage.setItem(THEME_KEY, theme) }
const savedTheme = localStorage.getItem(THEME_KEY) if (savedTheme) { setTheme(savedTheme) }
|
用 JavaScript 读写 CSS 变量
1 2 3 4 5 6 7 8 9 10
| const primary = getComputedStyle(document.documentElement) .getPropertyValue('--color-brand') .trim()
element.style.setProperty('--color-brand', '#ef4444')
element.style.removeProperty('--color-brand')
|
这个能力很有用,比如拖拽调整颜色、用户自定义主题色等场景:
1 2 3 4
| colorPicker.addEventListener('input', (e) => { document.documentElement.style.setProperty('--color-brand', e.target.value) })
|
响应式设计里的用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| :root { --font-size-base: 16px; --container-padding: 24px; --columns: 3; }
@media (max-width: 768px) { :root { --font-size-base: 14px; --container-padding: 16px; --columns: 1; } }
.container { padding: var(--container-padding); font-size: var(--font-size-base); }
.grid { display: grid; grid-template-columns: repeat(var(--columns), 1fr); }
|
在媒体查询里覆盖变量,只需要改一处,所有用到这个变量的地方都会跟着变。
组件级变量
CSS 变量的作用域可以限定在特定组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| .btn { --btn-bg: #3b82f6; --btn-color: white; --btn-padding: 8px 16px;
background: var(--btn-bg); color: var(--btn-color); padding: var(--btn-padding); }
.btn-danger { --btn-bg: #ef4444; }
.btn-ghost { --btn-bg: transparent; --btn-color: #3b82f6; }
.btn-large { --btn-padding: 12px 24px; }
|
这种写法比写很多条选择器覆盖属性要清晰很多,修改成本也低。
和 SCSS 一起用
CSS 变量和 SCSS 并不冲突,可以配合用:
1 2 3 4 5 6 7 8 9 10 11 12 13
| $colors: ( 'blue-500': #3b82f6, 'blue-400': #60a5fa, 'gray-900': #111827, );
:root { @each $name, $value in $colors { --color-#{$name}: #{$value}; } }
|
SCSS 负责组织和生成,CSS 变量负责运行时动态能力,各司其职。
CSS 变量的浏览器支持已经很好了,IE 不支持但你大概也不需要兼容 IE 了。新项目建议直接用起来,配合主题切换效果很好,改起来也方便。