CSS 自定义属性(CSS 变量)

深入理解 CSS 自定义属性的层叠、继承和计算机制,var() 的语法和备选值,以及如何用 CSS 变量构建可维护的设计系统。

CSS 自定义属性(CSS 变量)

CSS 变量是 CSS 的编程基础设施

CSS 变量(CSS Custom Properties)让 CSS 具备了编程能力。通过定义可复用的命名值,我们可以创建主题系统、实现响应式设计、在运行时动态修改样式,而不需要 JavaScript 操作 style 对象。

理解 CSS 变量的层叠规则、继承机制和计算过程,是构建现代设计系统的基础。

面试定位:CSS 变量是现代 CSS 开发的核心。面试官通过变量的层叠覆盖、var() 的备选值机制、以及变量与预处理器变量(如 Sass $var)的区别,问的是候选人对 CSS 机制的深层理解。


自定义属性的基础语法

定义和引用

/* 定义:-- 开头,冒号分隔 */
:root {
  --color-primary: #3b82f6;
  --spacing-md: 1rem;
  --font-size-base: 16px;
  --border-radius: 8px;
}

/* 引用:var() 函数 */
.button {
  background: var(--color-primary);
  padding: var(--spacing-md);
  font-size: var(--font-size-base);
  border-radius: var(--border-radius);
}

命名规范

/* 合法命名 */
--color-primary: blue;
--my-var: 10px;
--MY_VAR: 20px; /* 大小写敏感 */
--font-size-lg: 1.5rem;

/* 非法命名(以数字开头) */
/* --1color: blue; ❌ */

/* 推荐命名规范 */
--brand-primary: #3b82f6;
--space-unit: 8px;
--radius-small: 4px;
--radius-medium: 8px;
--radius-large: 16px;

var() 函数详解

基本用法

/* 直接引用 */
.element {
  color: var(--color-text);
  margin: var(--spacing-md);
}

/* 带备选值 */
.element {
  color: var(--color-text, #333); /* 变量未定义时使用备选值 */
  margin: var(--spacing-md, 1rem); /* 备选值可以是任何有效值 */
}

备选值机制

/* 备选值可以是多个 */
.element {
  /* 尝试 --color-link,其次 --color-primary,最后 #0066cc */
  color: var(--color-link, var(--color-primary, #0066cc));
}

/* 备选值支持复杂表达式 */
.element {
  /* 备选值中也可以使用 var() */
  padding: calc(var(--spacing-base, 1rem) * 2);
}

空值 vs 未定义

/* 变量未定义 */
.element {
  color: var(--undefined-color); /* 使用未定义的变量,效果等同于 auto/default */
}

/* 变量被设为空字符串 */
.element {
  --undefined-color: ; /* 空字符串是有效值 */
  color: var(--undefined-color, red); /* 会被解析 */
}

层叠与继承

自定义属性的层叠

/* 在 :root 定义 */
:root {
  --color: blue;
}

/* 在具体选择器中覆盖 */
.special {
  --color: red;
}

.normal {
  color: var(--color); /* 继承自 :root,blue */
}

.special {
  color: var(--color); /* 被覆盖,red */
}

自定义属性的继承

.parent {
  --text-color: black;
}

.child {
  color: var(--text-color); /* 继承父元素的 --text-color */
}

/* 没有定义 --text-color 的元素 */
.other {
  color: var(--text-color, gray); /* 回退到备选值 gray */
}

computed value 计算值

:root {
  --base-size: 16px;
}

.element {
  /* calc() 中使用变量 */
  font-size: calc(var(--base-size) * 1.5); /* 24px */

  /* 运算中的单位处理 */
  margin: calc(var(--base-size) * 2); /* 32px */
  padding: calc(var(--base-size) + 8px); /* 24px */
}

响应式设计中的应用

断点变量

:root {
  --container-width: 100%;
}

@media (min-width: 768px) {
  :root {
    --container-width: 720px;
  }
}

@media (min-width: 1024px) {
  :root {
    --container-width: 960px;
  }
}

.container {
  width: var(--container-width);
  margin: 0 auto;
}

主题切换

/* 亮色主题 */
:root {
  --bg-primary: #ffffff;
  --text-primary: #1a1a1a;
  --bg-secondary: #f5f5f5;
}

/* 暗色主题 */
[data-theme="dark"] {
  --bg-primary: #1a1a1a;
  --text-primary: #ffffff;
  --bg-secondary: #2d2d2d;
}

.page {
  background: var(--bg-primary);
  color: var(--text-primary);
}

组件变体

.button {
  /* 基础样式 */
  --button-bg: var(--color-primary);
  --button-text: white;
  --button-padding: 0.75rem 1.5rem;

  background: var(--button-bg);
  color: var(--button-text);
  padding: var(--button-padding);
}

/* 变体覆盖变量 */
.button--outline {
  --button-bg: transparent;
  --button-text: var(--color-primary);
  border: 1px solid var(--color-primary);
}

.button--large {
  --button-padding: 1rem 2rem;
  font-size: 1.125rem;
}

高级用法

条件变量(@supports)

@supports (color: oklch(0 0 0)) {
  :root {
    --color-primary: oklch(60% 0.2 250);
  }
}

@supports not (color: oklch(0 0 0)) {
  :root {
    --color-primary: #3b82f6;
  }
}

动态属性名

.element {
  --side: top;

  /* 动态构建属性名 */
  border-: var(--side): 1px solid red; /* border-top: 1px solid red */
}

与 JavaScript 交互

:root {
  --progress: 0;
}

.progress-bar {
  width: calc(var(--progress) * 1%);
}
// JavaScript 中修改 CSS 变量
const root = document.documentElement;
root.style.setProperty('--progress', 75);

CSS 变量 vs 预处理器变量

特性 CSS 变量 Sass $变量
运行时可用 否(编译时)
可继承
可在媒体查询中修改
可被 JavaScript 修改
可用 var() 动态引用
/* Sass:编译时替换 */
$color-primary: blue;
.button { color: $color-primary; }
/* 编译后:.button { color: blue; } */
/* CSS 变量:运行时替换 */
:root { --color-primary: blue; }
.button { color: var(--color-primary); }
/* 浏览器中仍然使用 var(),运行时解析 */

实战:设计系统变量结构

/* 1. 设计 Token */
:root {
  /* 颜色 */
  --color-brand-50: #eff6ff;
  --color-brand-100: #dbeafe;
  --color-brand-500: #3b82f6;
  --color-brand-900: #1e3a8a;

  /* 间距 */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 1rem;
  --space-4: 1.5rem;
  --space-6: 3rem;

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

/* 2. 语义变量 */
:root {
  --color-bg: var(--color-brand-50);
  --color-text: var(--color-brand-900);
  --color-accent: var(--color-brand-500);
}

/* 3. 组件变量 */
.button {
  --button-bg: var(--color-accent);
  --button-text: white;
  --button-radius: var(--radius-md);
  --button-padding: var(--space-2) var(--space-4);

  background: var(--button-bg);
  color: var(--button-text);
  padding: var(--button-padding);
  border-radius: var(--button-radius);
}

面试高频问题

Q: CSS 变量和 Sass 变量有什么区别?

回答要点:CSS 变量是运行时解析的,可继承、可通过 JavaScript 修改、可以在媒体查询中动态改变;Sass 变量是编译时替换的,不具备这些运行时能力。CSS 变量适合做主题系统和响应式设计,Sass 变量适合存储静态配置值。

Q: var() 的备选值机制是怎样的?

回答要点:var() 的第二个参数是备选值,当变量未定义或被设为无效值时使用。备选值可以是任何有效的 CSS 值,也可以是另一个 var()。这支持链式回退:var(--color, var(--fallback, red))

Q: 如何用 CSS 变量实现主题切换?

回答要点:在 :roothtml 上定义颜色变量,在 [data-theme="dark"] 等选择器中覆盖这些变量。JavaScript 只需要修改 document.documentElementdata-theme 属性即可切换主题,整个页面的颜色自动响应。


延展阅读