CSS 变量(自定义属性)

CSS 变量(正式名称为自定义属性,Custom Properties)让你在样式表中定义可复用的值。它们比预处理器变量(如 Sass 的 $color)更强大——因为 CSS 变量是动态的,可以在运行时改变。

声明与使用

声明变量

变量名以 -- 开头,通常定义在 :root 中作为全局变量:

css
:root {
  --color-primary: #2563eb;
  --color-bg: #f8fafc;
  --spacing-md: 1rem;
  --radius: 8px;
  --font-body: system-ui, sans-serif;
}

使用变量

var() 函数读取变量值:

css
.button {
  background: var(--color-primary);
  padding: var(--spacing-md);
  border-radius: var(--radius);
  font-family: var(--font-body);
}
html
<div style="--color-primary:#2563eb;--spacing-md:0.75rem 1.5rem;--radius:6px;--font-body:system-ui,sans-serif">
  <div class="button">按钮</div>
</div>

作用域

变量在其声明所在的元素及其所有后代中可用:

css
:root {
  --text-color: #1e293b; /* 全局 */
}

.dark-section {
  --text-color: #e2e8f0; /* 仅在此区块及其内部生效 */
  background: #0f172a;
  color: var(--text-color);
}

p {
  color: var(--text-color); /* 在 .dark-section 内是白色,外面是黑色 */
}
html
<p>我是黑色文字</p>
<div class="dark-section">
  <p>我是白色文字(变量被重载了)</p>
</div>

这就是 CSS 变量比预处理器变量强大的核心原因——可以在运行时基于选择器的上下文动态改变。这在实现主题切换(dark mode)、组件变体时是杀手级特性。

回退值

var() 的第二个参数作为回退值:

css
.card {
  background: var(--card-bg, white);
  /* 如果 --card-bg 未定义,使用 white */

  padding: var(--padding, 1rem 2rem);
}

回退值可以嵌套另一个 var()

css
.element {
  /* 先尝试 --primary,没有就尝试 --blue,最后用 #2563eb */
  color: var(--primary, var(--blue, #2563eb));
}

与 JavaScript 交互

CSS 变量的动态性在 JavaScript 中体现得最充分:

javascript
// 读取 CSS 变量
const primaryColor = getComputedStyle(document.documentElement)
  .getPropertyValue('--color-primary');

// 设置 CSS 变量(修改全局主题色)
document.documentElement.style.setProperty('--color-primary', '#ef4444');

// 移除变量
document.documentElement.style.removeProperty('--color-primary');

这使得主题切换变得极其简单:

css
:root {
  --bg: white;
  --text: #1e293b;
}

[data-theme="dark"] {
  --bg: #0f172a;
  --text: #e2e8f0;
}

body {
  background: var(--bg);
  color: var(--text);
}
javascript
// 一键切换主题
function toggleTheme() {
  const html = document.documentElement;
  const current = html.getAttribute('data-theme');
  html.setAttribute('data-theme', current === 'dark' ? 'light' : 'dark');
}

实际工程应用

设计令牌(Design Tokens)

将设计系统的所有可配置值定义为 CSS 变量,实现全站一致:

css
:root {
  /* 颜色 */
  --color-primary: #2563eb;
  --color-primary-hover: #1d4ed8;
  --color-success: #16a34a;
  --color-warning: #d97706;
  --color-danger: #dc2626;

  /* 文字 */
  --color-text: #1e293b;
  --color-text-secondary: #64748b;
  --color-border: #e2e8f0;

  /* 间距 */
  --space-xs: 0.25rem;
  --space-sm: 0.5rem;
  --space-md: 1rem;
  --space-lg: 1.5rem;
  --space-xl: 2rem;
  --space-2xl: 3rem;

  /* 字体 */
  --font-sans: system-ui, -apple-system, sans-serif;
  --font-mono: 'JetBrains Mono', 'Consolas', monospace;

  /* 字号 */
  --text-sm: 0.875rem;
  --text-base: 1rem;
  --text-lg: 1.125rem;
  --text-xl: 1.25rem;
  --text-2xl: 1.5rem;
  --text-3xl: 1.875rem;

  /* 圆角 */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-full: 9999px;

  /* 阴影 */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
  --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07);
  --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
}

组件变体

css
.btn {
  --btn-bg: var(--color-primary);
  --btn-color: white;
  background: var(--btn-bg);
  color: var(--btn-color);
  padding: var(--space-sm) var(--space-lg);
  border-radius: var(--radius-md);
}

/* 变体:修改局部变量 */
.btn-danger {
  --btn-bg: var(--color-danger);
}

.btn-ghost {
  --btn-bg: transparent;
  --btn-color: var(--color-primary);
  border: 1px solid var(--color-primary);
}
html
<div style="--color-primary:#2563eb;--color-danger:#ef4444;--space-sm:0.5rem;--space-lg:1.5rem;--radius-md:6px">
  <button class="btn">默认按钮</button>
  <button class="btn btn-danger">危险按钮</button>
  <button class="btn btn-ghost">幽灵按钮</button>
</div>

calc() 结合变量

css
.sidebar-layout {
  --sidebar-width: 250px;
  display: grid;
  grid-template-columns: var(--sidebar-width) 1fr;
  /* 或动态计算 */
  grid-template-columns: calc(var(--sidebar-width) + 2rem) 1fr;
}

CSS 变量不能直接用于媒体查询——@media (min-width: var(--bp-md)) 是无效的。也不能在 url() 中直接拼接:url(var(--path)/image.png) 不会工作。

CSS 变量 vs 预处理器变量

特性CSS 变量Sass/Less 变量
运行时可变否(编译后替换为值)
作用域CSS 级联代码块
JS 可读写
可继承
媒体查询中可用是(编译时)

两者可以互补使用:预处理器变量管理编译时的常量(如数学运算、颜色函数),CSS 变量管理运行时的主题和动态值。