Appearance
现代CSS语法
元信息
- 目标:把 CSS 当一门"可编程的样式语言"——掌握原生自定义属性(变量)与 calc/clamp/color-mix/light-dark 等函数,实现主题切换与派生配色,无需任何构建工具;并认清预处理器(SCSS)的历史角色
- 关键概念:自定义属性、var()、calc、clamp、color-mix、主题变量、预处理器、转译
- 关联阶段:cp1
- 常见误区:以为 CSS 变量必须靠 SCSS 等构建工具(原生
--x/var()运行时可用且可继承);以为深浅主题切换要写两套样式或改 HTML(变量覆盖一处、全站跟随);以为颜色只能一个个挑(color-mix 能从品牌色派生全家)
为什么 CSS 需要"可编程"
在本周主线三「可编程 CSS」的地图上,本篇是本体:这门样式语言长出了程序设计特性——指令的值不再写死,而是算出来的。
设想全站换品牌色的场景:色值写死在样式表里,出现在链接、按钮、边框、背景……共 47 处。逐处手改既枯燥又容易漏。早期 CSS 缺乏程序设计特性——没有变量、没有函数、没有控制结构,这正是 SCSS 等预处理器诞生的原因(文末有它的完整故事)。
而最近几年的 CSS 标准已经原生补齐了这些能力:变量、数学函数、颜色函数、嵌套,写进样式表就能在当代浏览器中运行,无需任何构建工具。本篇介绍这套可编程特性,它们是现代 CSS 的主轴。
自定义属性:CSS 的变量
自定义属性(custom property)以 -- 开头,用 var() 函数取用:
:root 即文档根元素,在这里声明的变量全局可用,相当于整站的设计常量——把主题色、间距、圆角集中在一处,改一处即全站生效。回到开头的场景:47 处色值收成一个 --brand-color,换色就是改一行。
动手:把示例中 --brand-color 的值换掉,Run 之后数一数页面上几处跟着变。
变量是"活的":继承与作用域
自定义属性沿 DOM 树继承——继承正是裁决链的一环(见CSS基础),且可以在任何元素上重新声明——同一个变量在不同子树里可以取不同的值,这就是主题变量的原理。还是那张诗卡:
切主题 = 切换一个属性值,所有样式自动跟随。第 3 周示例"一曲新词酒一杯"的主题切换行为,最现代的实现方式正是这一招——也再次印证"样式与结构分离":HTML 结构原样不动。var() 还支持缺省值,变量未定义时不至于失效:
css
.button { color: var(--btn-text, white); } /* 未定义 --btn-text 时用 white */组件级主题
把变量声明收窄到某个类上,同一套组件就能在不同容器里呈现不同外观:
css
.card { --card-bg: #f6f8fa; --card-border: #d0d7de; }
.card--warning { --card-bg: #fff8c6; --card-border: #d4a72c; }这套"变量 + 覆盖"的模式,就是 design token 思想(第 14 周)的最小雏形。
颜色的表示与派生
颜色的写法本身也在演化,一条线是从"名字"走向"数值":
css
color: red; /* 颜色名:直白但只有 140 多个 */
color: #ff0000; /* 16 进制:最常用 */
color: rgb(255, 0, 0); /* 红绿蓝分量 */
color: oklch(0.63 0.25 29); /* 感知均匀的新空间:调亮度时色相不跑偏 */另一条线是从"孤立的色值"走向"派生的色系"。color-mix() 从一个基准色按比例调出深浅透明:
配色从"一堆孤立色值"变成"一个品牌色派生全家",可维护性质变。深浅主题还有一个一步到位的函数——light-dark() 配合 color-scheme,一行声明同时给出两个世界的值:
css
:root {
color-scheme: light dark;
--text: light-dark(#222, #eaeaea);
}两个提醒:其一,配色别随机试,应基于配色方案或设计系统(延伸一节列了入口);其二,同一色值在不同显示器上会有色差——颜色最终由物理设备呈现,电商"色差免责"条款的由来,显示器差异是行业现实而不是代码 bug。
SCSS:预处理器路线
CSS 补齐编程特性的路有两条,理解它们的消长很有价值:
- 升级 CSS 本身:自定义属性、函数、嵌套进标准,浏览器原生运行——这条路已经胜出
- 预处理器:发明一门类 CSS 的新语言(SCSS/LESS/Stylus),写完由工具转译(transpile)回普通 CSS
SCSS 是预处理器的代表。变量用 $,支持嵌套:
css
// nav.scss —— SCSS 源码
$nav-color: #f90;
nav {
width: 100px;
a { color: $nav-color; } /* 嵌套:相当于 nav a { ... } */
}css
/* 转译后的 nav.css —— 浏览器实际收到的 */
nav { width: 100px; }
nav a { color: #f90; }关键差异在与原生能力的对照:
| 能力 | 原生 CSS | SCSS |
|---|---|---|
| 变量 | --x + var(x),运行时、可继承 | $x,编译时、编译后即消失 |
| 函数 | calc/min/max/clamp/color-mix 等 | 自定义 @function |
| 主题切换 | 变量覆盖,零成本 | 需输出多套类 |
| 依赖 | 无,浏览器直接运行 | 需构建工具转译 |
原生变量的"运行时 + 继承"是 SCSS 编译期变量做不到的——比如上面 [data-theme="dark"] 的运行时覆盖,SCSS 变量编译后就不存在了。这是"新项目优先原生"的根本原因。SCSS 仍有价值的地方是它的写法习惯与庞大的存量项目——今天读老项目要认识它,新项目则先问"原生够不够"。顺带一笔:SCSS 的嵌套写法也回归了原生,CSS Nesting 已进标准,nav { a { … } } 直接写在 .css 文件里就能跑——三大招牌(变量、函数、嵌套)至此全部收编。转译的思路你还会再遇到:第 13 周的构建工具链、babel 转译新版本 JavaScript,都是同一件事。
小结
- 自定义属性
--x+var()是运行时的真变量:可继承、可覆盖,主题切换一处改全站跟随 - calc/min/max/clamp 让尺寸可计算,clamp 一个顶过去几段媒体查询
- 颜色从写死走向派生:color-mix 出全家、light-dark 出双主题、oklch 感知均匀
- SCSS 是"CSS 缺编程特性"年代的补丁路线,原生补齐后转为读老项目要认识的方言
延伸
- 布局相关的现代特性(grid、容器查询)归第 6 周
- 变量 + 覆盖的模式走到组件体系层面,就是 design token,归第 14 周
- 原生嵌套语法(CSS Nesting)已进标准,可查 MDN CSS 嵌套
- 配色方案与色彩系统 https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_colors