SCSS不支持:has()等现代伪类嵌套,因其属浏览器运行时特性,应交由PostCSS处理;强行在&后拼接会报错,正确做法是写独立选择器.card:has(.badge)。

不是 SCSS 不支持新 CSS 语法,而是你把本该由 PostCSS 处理的特性,硬塞进了 Sass 编译器里。
为什么 :has()、:is() 在 SCSS 文件里直接写就报错
Dart Sass 的任务只是把 @use、$var、嵌套这些预处理逻辑转成纯 CSS 字符串,它不负责验证生成的 CSS 是否合法。当你写:.card { &:has(.badge) { padding: 1rem; } },Sass 尝试把它当 SCSS 嵌套解析——但 :has() 根本不是 SCSS 支持的选择器片段,它属于浏览器运行时才理解的原生伪类。结果就是 Expected selector 或 Invalid CSS after "&:"。
- 所有以
:开头的现代伪类(:has、:is、:where、:host)必须作为完整选择器出现,不能跟在&后面拼接 - 强行保留嵌套写法,只能用引号包裹:
"&:has(.badge)",但这已失去嵌套语义,且无法访问父作用域变量 - 真正该写的位置是:独立选择器块,例如
.card:has(.badge) { ... }
@supports 里写 selector() 函数为什么会报 Undefined function
@supports (selector(:has(*))) 看起来像 CSS 功能查询,但它用到了 selector() 这个 CSS 函数——而 Dart Sass 完全不认识这个函数,它只认自己内置的 math.div()、type-of() 这类。一旦遇到不认识的函数名,就直接抛 Undefined function。
-
@supports是 CSS 原生规则,不是 Sass 控制结构;Sass 不会“执行”它,只会原样输出到 CSS 中 - 如果你需要根据构建环境开关样式,改用 Sass 变量 +
@if:@if $supports-has { .card { ... } },再通过 Vite 的define或 Webpack 的DefinePlugin注入该变量 - 若必须透传
@supports,确保整行不带任何 Sass 插值或函数调用,例如:@supports (display: grid) { ... }是安全的
Vite 升级后 :has() 报错变多,其实是 loader 执行顺序错了
Vite 5.0+ 默认使用 sass-embedded,它输出的是标准 CSS 字符串;如果后续没接上 PostCSS,或者 PostCSS 插件没启用 postcss-preset-env,那些新语法就会裸奔进浏览器——而浏览器版本低时直接忽略,但构建阶段不会报错;反而是某些配置错误会让 Sass 提前拦截。
立即学习“前端免费学习笔记(深入)”;
- 检查
postcss.config.js是否存在,且包含postcss-preset-env,stage至少为4 - 确认 Vite 的
css.postcss配置没有被覆盖,尤其不要在vite.config.ts里重复定义postcss导致插件未加载 - Vue 项目中,若在
<style lang="scss">里写了:has(),确保sass和postcss的 loader 链顺序是:vue-loader → sass → postcss,而不是反过来
最常被忽略的一点:你以为在写 CSS,其实正在混合两个不同抽象层的语法。Sass 层管“怎么生成 CSS”,CSS 层管“浏览器怎么解释 CSS”。把后者的事交给前者做,等于让厨师去调试烤箱电路板——工具链分工没理清,再新的语法也救不了。


















