Skip to content

现代CSS语法

元信息

  • 目标:把 CSS 当一门"可编程的样式语言"——掌握原生自定义属性(变量)与 calc/clamp/color-mix/light-dark 等函数,实现主题切换与派生配色,无需任何构建工具
  • 关键概念:自定义属性、var()、calc、clamp、color-mix、主题变量
  • 关联阶段:cp1
  • 常见误区:以为 CSS 变量必须靠 SCSS 等构建工具(原生 --x/var() 运行时可用且可继承);以为深浅主题切换要写两套样式或改 HTML(变量覆盖一处、全站跟随)

CSS 基础一篇里提到,早期 CSS 缺乏程序设计特性(变量、函数、控制结构),预处理器正是为弥补这一短板而生。而最近几年的 CSS 标准已经原生补齐了这些能力——本篇介绍现代 CSS 的可编程特性,它们无需任何构建工具,写进样式表就能在当代浏览器中运行。

自定义属性:CSS 的变量

声明与使用

自定义属性(custom property)以 -- 开头,用 var() 函数取用:

css
:root {
  --brand-color: #1a73e8;
  --text-color: #222;
  --spacing: 8px;
}

body {
  color: var(--text-color);
}

a {
  color: var(--brand-color);
}

.card {
  padding: calc(var(--spacing) * 2);
}

:root 即文档根元素,在这里声明的变量全局可用,相当于整站的设计常量——把主题色、间距、圆角集中在一处,改一处即全站生效。

变量是"活的":继承与作用域

自定义属性沿 DOM 树继承,且可以在任何元素上重新声明——于是同一个变量在不同子树里可以有不同的值,这就是主题变量的原理:

css
:root {
  --bg: #ffffff;
  --text: #222222;
}

/* 深色主题:只覆盖变量,不写任何新规则 */
[data-theme="dark"] {
  --bg: #1a1a2e;
  --text: #eaeaea;
}

body {
  background: var(--bg);
  color: var(--text);
}

切主题 = 切换一个属性值,所有样式自动跟随。第 3 周示例"一曲新词酒一杯"的主题切换行为,最现代的实现方式正是这一招——也再次印证了"样式与结构分离":HTML 一个 data-theme 属性都不用改。

var() 还支持缺省值,变量未定义时不至于失效:

css
.button {
  color: var(--btn-text, white);  /* 未定义 --btn-text 时用 white */
}

组件级主题

把变量声明收窄到某个类上,就得到"组件级主题"——同一套组件,不同容器里呈现不同外观:

css
.card {
  --card-bg: #f6f8fa;
  --card-border: #d0d7de;
  background: var(--card-bg);
  border: 1px solid var(--card-border);
}

.card--warning {
  --card-bg: #fff8c6;
  --card-border: #d4a72c;
}

这套"变量 + 覆盖"的模式,就是 design token 思想(第 14 周)的最小雏形。

原生函数:会计算的样式

现代 CSS 内置了一批函数,让样式值可以算出来而不是写死:

数学计算:calc / min / max / clamp

css
.content {
  /* 屏幕宽度减去两侧留白 */
  width: calc(100% - 2rem);
}

.title {
  /* 不小于 16px,不大于 32px,中间随视口流动 */
  font-size: clamp(16px, 2vw + 8px, 32px);
}

.sidebar {
  /* 取理想宽度和可用宽度中较小者,永不溢出 */
  width: min(300px, 100%);
}

clamp(最小, 理想, 最大) 一个函数就实现了流动排版——过去要靠媒体查询写好几段。

颜色函数:相对颜色与混色

css
:root {
  --brand: #1a73e8;
}

.button {
  background: var(--brand);
  /* 悬停时同色变深 20%:从品牌色派生,不必另挑一个色值 */
}

.button:hover {
  background: color-mix(in srgb, var(--brand) 80%, black);
}

.badge {
  /* 品牌色的半透明版 */
  color: var(--brand);
  background: color-mix(in srgb, var(--brand) 12%, transparent);
}

颜色系统从"一堆孤立色值"变成"一个品牌色派生全家"——配色的可维护性质变。此外 light-dark() 函数配合 color-scheme 属性,一行声明同时给出浅色/深色两个值:

css
:root {
  color-scheme: light dark;
  --text: light-dark(#222, #eaeaea);
}

与 SCSS 的对照

能力原生 CSSSCSS
变量--x + var(x),运行时、可继承$x,编译时、不可变
函数calc/min/max/clamp/color-mix 等自定义 @function
主题切换变量覆盖,零成本需输出多套类
依赖无,浏览器直接运行需构建工具转译

原生变量的"运行时 + 继承"特性是 SCSS 编译期变量做不到的(SCSS 变量编译后就不存在了),这是"新项目优先原生"的根本原因。SCSS 仍有价值的部分是其嵌套语法和工程化生态,见SCSS

边界

  • 布局相关的现代特性(grid、容器查询等)归第 6 周
  • 变量 + 覆盖的模式走到组件体系层面,就是 design token,归第 14 周