sass 中选择器开头不能直接使用插值(如 .#{$var}),因解析阶段要求起始符为字母、下划线或 &,插值尚未展开即被视为空起始,导致 “invalid css after '.'” 错误;安全写法为固定前缀+插值(如 .btn-#{$type})或 & + @at-root 组合;map 或动态值需 quote() 包裹防解析错误,且插值不校验 css 合法性,易产生静默无效规则。

直接在选择器开头写 .#{$var} 会编译失败,Sass 解析器在语法分析阶段就拒绝这种写法——它要求选择器起始必须是字母、下划线或 &,而插值内容此时尚未展开,被视为空起始。
为什么 .#{$suffix} 会报错 “Invalid CSS after ‘.’”
这不是变量未定义的问题,而是 Sass 的解析机制决定的:#{$suffix} 出现在选择器最前面时,Sass 还没走到“求值”阶段,根本不知道它会展开成什么。它只看到一个孤立的 . 后面跟着非法起始符号,直接抛出 Invalid CSS after ".": expected class name。
- 哪怕
$suffix: "header"已正确定义,.#{$suffix}依然报错 - 错误发生在编译早期(parser 阶段),不是运行时报错,也和浏览器无关
- 类似写法如
#{$id}、[data-#{$attr}="x"]同样不被允许——只要插值是选择器的“首字符”,就过不了关
真正能用的两种安全模式
只有把插值“嵌套在合法前缀之后”,才能绕过解析限制。核心思路是:让 Sass 看到一个结构完整的起始部分,再把变量拼在后面。
-
固定前缀 + 插值:写成
.btn-#{$type}或.icon-#{$name},前提是$type和$name是已定义字符串,且拼出的结果符合 CSS 类名规范(不能以数字开头,不含空格) -
&+@at-root组合:在已有选择器内用&引用父级,再用@at-root提升作用域生成平级规则。例如:@mixin gen-class($name) { &__#{$name} { @at-root .#{$name}-item { color: red; } } } .card { @include gen-class("header"); }编译后输出.header-item { color: red; },而非嵌套在.card__header下
带破折号、数字开头或 map 动态值怎么处理
从 map-get($sizes, "sm") 拿到的 "max-sm" 或 "2xl" 直接插值会出问题:破折号被当减号、数字开头被当非法标识符。必须用 quote() 包裹,强制转为字符串字面量。
- 错误:
.size-#{$size},其中$size: "max-sm"→ Sass 尝试计算max - sm,报Undefined variable "max" - 正确:
.size-#{quote($size)}→ 输出.size-"max-sm"(注意引号会被保留在 CSS 中,但浏览器能识别) - 更稳妥做法是提前校验并标准化:
$safe-size: str-replace($size, "-", "_"),再插值 - 如果值来自
@each循环,确保 key 是合法标识符,或统一加quote()
容易被忽略的三个实际坑点
插值本身语法简单,但产出的是静态字符串,编译器不校验 CSS 合法性,浏览器静默忽略无效规则,调试极难定位。
-
.foo-#{$size} -bar中多了一个空格 → 输出.foo-md -bar(两个独立选择器),不是你想的.foo-md-bar -
$prefix拼写错误或作用域不可见 → 插值结果为空 →.-btn这种非法类名,CSS 无效但不报错 - 用
@extend %placeholder-#{$state}可行,但@include mixin-#{$name}完全不支持——插值不能用于 mixin 名、function 名或控制指令名
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











