用 SCSS 的 @each 配合 :root 块 + map-keys() 和 map-get() 可将嵌套 Token Map 转为 CSS 自定义属性,但需递归扁平化、剥离单位、颜色转 rgb()、用 class 控制深色模式,并注意编译期静态性限制。

直接说结论:用 SCSS 的 @each 配合 :root 块 + map-keys() 和 map-get(),就能把嵌套的 Token Map 一次性转成 CSS 自定义属性;但必须注意单位剥离、颜色自动转 rgb() 字符串、以及深色模式等上下文覆盖逻辑。
为什么不能直接 @each $key, $value in $tokens?
因为设计 Token 通常按语义分层(比如 $colors: ("primary": #007bff, "text": ("base": #333, "muted": #666))),直接遍历顶层会漏掉嵌套值,且生成的 CSS 变量名会变成 --text: [object Object] 这种无效结果。
实操建议:
- 先用递归函数扁平化嵌套 Map,例如写一个
flatten-map($map, $prefix: ""),把"text.base"作为键名输出 - 确保所有值都是可序列化的原始类型:
number、color、string;避免传入calc()表达式或函数调用 - 对
color类型值,用ie-hex-str()或str-slice(#{"#"} + inspect($color), 2)提取十六进制,或用rgba($color, 1)转成函数形式供 JS 读取
如何安全处理带单位的数值(如 12px、1.5rem)?
CSS 自定义属性本身不解析单位,--size-sm: 12px 是合法的字符串,但若后续在 calc() 中直接使用(如 width: calc(var(--size-sm) * 2)),会因缺少上下文单位报错。
实操建议:
- 统一剥离单位,只存纯数字,再额外存一份带单位的变量(如
--size-sm-value: 12+--size-sm-unit: px) - 或改用
clamp()/em等相对单位预设,避免运行时计算依赖 - SCSS 层面可用
unit($value) == "px"判断后用strip-units($value)(需自行定义该函数)提取数值
怎么让深色模式 Token 自动覆盖根变量?
不能靠 @media (prefers-color-scheme: dark) 直接覆盖 —— 它只响应系统偏好,无法适配手动切换的深色主题。真正可控的方式是加一层 class 控制器。
实操建议:
- 在
:root中声明默认(浅色)Token,再单独写.theme-dark :root块重写变量 - Token Map 分离定义:
$tokens-light和$tokens-dark,用同一个扁平化函数分别输出 - 避免在
:root外使用var(--color-primary),否则深色 class 切换后,已渲染的组件不会重算(CSS 变量是静态继承的)
最常被忽略的一点:SCSS 编译期生成的变量名是静态字符串,无法动态响应运行时主题切换;如果项目用 JS 控制主题,就得让 JS 同时写入 document.documentElement.style 来补全缺失的变量,或者干脆放弃纯 CSS 方案,改用 CSS-in-JS 的 runtime 计算逻辑。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











