写代码时总碰上一些摸不着头脑的CSS新规则?2024年一口气冒出7个@规则,比过去十年加起来还多。本文扒一扒这些新玩意的来头,顺便手把手教大伙用@supports和JavaScript精准检测浏览器是否支持某个at-rule,避免样式翻车。
啥是CSS at-rule
CSS里的at-rule就是以@符号打头的指令,比如@media、@import、@keyframes。它们就像给浏览器下的“特别命令”,告诉浏览器在特定条件下执行某套样式。2024年之前,主流浏览器支持的at-rule总共也就11个左右,结果今年直接炸出7个新成员——@scope、@starting-style、@property这些狠角色全来了。
拿@property举个例子,它用来定义自定义CSS变量(也叫CSS Houdini变量),能让变量拥有类型、初始值、甚至支持动画。要是浏览器不认识这货,整个自定义变量就会失灵。所以检测at-rule的支持情况就成了刚需。
历史快照:为啥以前没人管
1998年CSS 2标准发布时,只有@import和@page两个at-rule。到2011年CSS 2.1才加了个@media。中间虽然冒出了@font-face、@keyframes,但都是各自模块单飞,没有一个统一的大版本。那会儿大伙写样式基本不用操心兼容性,因为at-rule太少,浏览器支持也早烂大街了。
2011年@supports规则诞生,它的设计初衷是检测CSS属性和值,比如display: grid浏览器认不认。压根没人想到要去检测at-rule,因为当年根本就没几个at-rule需要测。直到2024年,事情变了——Chrome团队突然把at-rule()函数的状态从“New”改成了“Assigned”,说明官方也意识到这个需求迫在眉睫。
手工检测流程
下面直接上硬核操作,分两套方案:一套纯CSS(但有局限),另一套JS兜底。
方案A:用@supports测属性值
@supports虽然不能直接测at-rule,但可以曲线救国。很多新at-rule都会伴随一个专属CSS属性,比如@property一般会配合--*自定义属性使用。写一个检测块:
@supports (--my-var: initial) {
/* 如果浏览器认识自定义属性,说明@property大概率也支持 */
.box { background: green; }
}注意,这种办法只能算“侧面敲击”,不保真。比如@starting-style没有绑定特定属性,这招就废了。实际操作中需要先确认目标at-rule是否有关联属性——查MDN或者caniuse的数据比较靠谱。
方案B:JS动态注入+错误捕获
更精准的姿势是用JavaScript创建样式表,尝试插入at-rule,然后监听有没有报错。写一个检测函数:
function isAtRuleSupported(ruleText) {
const style = document.createElement('style');
style.textContent = ruleText;
try {
document.head.appendChild(style);
// 插入成功说明浏览器解析了这个规则
const isSupported = style.sheet.cssRules.length > 0;
style.remove();
return isSupported;
} catch (e) {
// 抛异常说明不认得这条规则
style.remove();
return false;
}
}
// 检测@scope
console.log(isAtRuleSupported('@scope (.a) to (.b) { .c { color: red; } }'));
// 检测@starting-style
console.log(isAtRuleSupported('@starting-style { .box { opacity: 0; } }'));这段代码的核心逻辑是:把at-rule塞进<style>标签,浏览器能解析就说明支持,解析不了就报错。有个坑必须留意——某些at-rule有语法限制,比如@import必须放在样式表最顶部,单独扔进style标签可能会被浏览器无视而不是报错。针对@import,可以用CSS.supports() API单独处理:
// 更干净的写法,适用于CSS.supports()能认的at-rule
console.log(CSS.supports('@import url("a.css")')); // 返回true或false实测发现CSS.supports()对@import、@namespace支持良好,但对@scope这类新规则可能返回false误报。所以最稳的组合拳是:先试CSS.supports(),如果返回false再跑一遍try-catch注入法。
分步实战:检测@scope
@scope是2024年的重磅新规,用来限定选择器的作用范围。假设要在一个项目里使用它,得先确认浏览器环境是否支持。
第一步,打开浏览器控制台(F12),切到Console面板。
第二步,输入以下检测代码:
function testScope() {
const style = document.createElement('style');
style.textContent = '@scope (.container) { .inner { color: red; } }';
document.head.appendChild(style);
const hasRule = style.sheet.cssRules.length > 0;
style.remove();
return hasRule;
}
console.log(testScope() ? '支持@scope' : '不支持@scope');如果返回true,说明当前浏览器(比如Chrome 118+)认得@scope。返回false就要准备降级方案。
第三步,写降级样式。利用@supports配合一个浏览器不认识的属性值来兜底:
/* 高级样式 */
@scope (.card) to (.card-footer) {
.title { font-size: 2rem; }
}
/* 降级方案:老浏览器直接全局样式 */
.title { font-size: 1.5rem; }注意,降级样式必须写在前面,防止覆盖。或者用@supports not来包裹老样式:
@supports not (selector(:scope)) {
/* 连:scope伪类都不认的浏览器,直接全局伺候 */
.title { font-size: 1.5rem; }
}这里有个隐藏雷区:@supports本身也有兼容性问题(IE完全无视),但IE已经退环境了,现代浏览器基本都支持。
表格速查:主流浏览器at-rule支持时间
| 规则名 | 首发年份 | Chrome | Firefox | Safari |
|---|---|---|---|---|
| @import | 1998 | 1+ | 1+ | 1+ |
| @media | 2011 | 1+ | 1+ | 1+ |
| @supports | 2013 | 28+ | 22+ | 9+ |
| @property | 2024 | 85+ | 128+ | 17.4+ |
| @scope | 2024 | 118+ | 128+ | 17.4+ |
数据来自caniuse整理,注意Firefox 128是2024年7月发布的,Safari 17.4也是今年春季更新。这说明2024年确实是at-rule的“大年”。
备胎方案:polyfill垫片库
如果项目必须兼容老掉牙的浏览器(比如Chrome 80以下),可以考虑引入polyfill。搜一下“css-scope-polyfill”或者“css-supports-polyfill”,这些库能在JS层面模拟部分at-rule的行为。不过要注意,polyfill往往有性能损耗,而且对语法糖类规则(比如@scope)模拟不完全。最佳实践是只在确认需要降级的环境才动态加载polyfill:
if (!isAtRuleSupported('@scope (.a) {}')) {
const script = document.createElement('script');
script.src = 'https://polyfill.example/scope.js';
document.head.appendChild(script);
}实际生产环境建议把polyfill文件放到自己的服务器上,避免引用外部不可控资源。另外,2024年出的at-rule大多数只在最新浏览器里能用,项目里使用前最好先统计一下用户浏览器版本分布——别为了炫技把线上页面搞崩了。
结尾彩蛋:未来会有at-rule()函数
Chrome团队已经把at-rule()函数的状态从“New”改成“Assigned”,意味着不久的将来可以直接用@supports at-rule(@scope)这种写法来检测。到时候上面那些JS骚操作就可以扔进历史博物馆了。不过在那之前,该写的检测代码还是得老老实实写。
