← 返回文章列表
CSS·1718 次阅读

CSS 自定义属性实战:从换肤到组件设计

最近给项目加深色模式,趁机把 CSS 自定义属性(CSS Custom Properties,俗称 CSS 变量)用了一遍。用下来最大的感受:它和 Sass 变量根本不是一回事,能力边界完全不同。

基本语法

声明在 -- 开头的属性上,用 var() 引用:

:root {
  --primary-color: #409eff;
  --radius: 4px;
}

.button {
  background: var(--primary-color);
  border-radius: var(--radius);
}

var() 支持兜底值,多级兜底也可以:

.title {
  /* --title-color 不存在就用 --text-color,再不行就用 #333 */
  color: var(--title-color, var(--text-color, #333));
}

注意一个坑:无效值不会回退到兜底,而是变成"未设置"。

:root {
  --size: 20px;
}
.box {
  --size: banana; /* 无效但语法上会被接受 */
  width: var(--size, 50px); /* 最终 width 是 unset,不是 50px! */
}

这是 CSS 变量最让人迷惑的行为,排查时留意。

和 Sass 变量的本质区别

很多人(包括曾经的我)以为 CSS 变量就是"Sass 变量的浏览器版",其实两者的运行机制完全不同:

Sass 变量 CSS 变量
生效时机 编译时,替换完就没了 运行时,真实存在于 DOM
JS 能否读写 不能 能
能否响应媒体查询 不能 能
作用域 文件级 跟随 DOM 树层叠

运行时存在这一点,是 CSS 变量的杀手锏。

实战一:零 JS 的响应式变量

变量本身可以跟随媒体查询变化,消费方完全无感:

:root {
  --content-width: 1200px;
  --font-size: 16px;
}

@media (max-width: 768px) {
  :root {
    --content-width: 100%;
    --font-size: 14px;
  }
}

.container {
  max-width: var(--content-width);
  font-size: var(--font-size);
}

业务代码里一处 var(),样式在两端自动切换,比写一堆媒体查询覆盖干净多了。

实战二:JS 驱动的主题切换

读写 CSS 变量的 API 很直白:

// 读取
const color = getComputedStyle(document.documentElement)
  .getPropertyValue('--primary-color')

// 写入
document.documentElement.style.setProperty('--primary-color', '#67c23a')

配合 class 切换,一套完整的换肤方案长这样:

/* 默认(浅色)主题 */
:root {
  --bg: #ffffff;
  --text: #303133;
}

/* 深色主题 */
.dark {
  --bg: #1a1a1a;
  --text: #e4e7eb;
}

body {
  background: var(--bg);
  color: var(--text);
  transition: background 0.3s, color 0.3s;
}
// 切换主题就是切一个 class
document.documentElement.classList.toggle('dark')

变量层叠覆盖 + 一个 class 切换,所有用了 var(--bg) 的地方瞬间换装。这就是 Element Plus 2.x 之后采用的方案(它们 1.x 时代还是编译多套 CSS 文件,体积巨大)。

实战三:组件样式的"开口"

写通用组件时,CSS 变量是留定制口的绝佳方式:

.card {
  padding: var(--card-padding, 16px);
  box-shadow: var(--card-shadow, 0 2px 8px rgba(0, 0, 0, 0.1));
}

使用方不用 hack 内部结构,直接在外层覆盖变量:

.marketing-page .card {
  --card-padding: 32px;
  --card-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
}

比 ::v-deep 穿透、比 !important 都优雅,还保持了组件的封装性。

小结

  • CSS 变量是运行时特性,能响应媒体查询、能被 JS 读写——这是它和 Sass 变量的本质区别
  • var() 的兜底只在变量不存在时生效,无效值不会回退
  • 主题切换:变量定义在 :root / .dark,切 class 即可
  • 组件定制:默认值写在 var() 里,使用方从外层覆盖变量

新项目里我的建议是:Sass 变量管"编译期常量"(断点值、mixin 参数),CSS 变量管"运行时可变的主题与配置",两者配合着用。

看到这里了?如果这篇文章帮到过你

请我喝瓶可乐就好 🥤

收款码

评论0 条

还没有评论,来抢沙发~

发表评论

评论需审核后显示

© 2026 许冬博客 · 专注 JS 技术分享

·RSS 订阅·Powered by Nuxt 4