学习 CSS At Rules 作用域样式 — @scope

CSS 社区模块化方案

概述简介

在前端快速的发展中, CSS 一直缺乏模块化的概念。由于 CSS 是根据选择器进行全局元素的匹配,它没有作用域可言。但随着项目越来越大,参与的人越来越多,命名冲突的问题日趋严重。于是 CSS 社区逐渐诞生了相应的模块化解决方案:Atomic CSSOOCSSBEMSMACSSITCSSCSS Modules 以及 CSS-In-JS

模块化分类

  • CSS Namespace: 通过人工约定命名规则
  • CSS Modules: 一个 CSS 文件,一个独立的模块
  • CSS-In-JS: 在 Javascript 中写 CSS

CSS 原生作用域方案

上述中,我们讲解了来自于 CSS 社区的模块化方案,但它们都需要额外的工具或者语法,对于小型项目来说,使用起来还是比较麻烦。但现在有了 CSS 原生作用域的提案,它提供了一种新语法 @scope 来实现局部作用域,或许 CSS 样式隔离的新时代即将到来。 (确切来说,是选择器隔离)

概念说明

来自 https://drafts.csswg.org/css-cascade-6/#at-ruledef-scope

A scope is a subtree or fragment of a document, which can be used by selectors for more targeted matching. A scope is formed by determining:

  • The scoping root node, which acts as the upper bound of the scope, and optionally:
  • The scoping limit elements, which act as the lower bounds.

An element is in scope if:

  • It is an inclusive descendant of the scoping root, and
  • It is not an inclusive descendant of a scoping limit.

作用域可以是 document 的子树或片段,选择器使用它进行更有针对性的匹配。作用域的范围通过如下两点确定:

  • 作用域根节点,它作为作用域的上限
  • 作用域限制元素,它作为作用域的下限(可选)

如果同时满足以下两种情况,则元素在作用域范围中:

  • 它是作用域根节点的 包容性后代
  • 它不是作用域限制元素的 包容性后代

语法定义

作用域样式属于 CSS At Rules 中一种,在 CSS 语法中使用 @scope 进行描述,它声明了与一组样式规则关联的作用域根节点和作用域限制元素。 它具有近度优先的特性 (Proximity precedence)

css 复制代码
  /* 定义 */
  @scope [(<scope-start>)]? [to (<scope-end>)]? {
    /* <rule-list> */
  }
  
  
  /* 例 1 */
  @scope (.card) {
    :scope { /* 匹配 .card 自身样式 */ }
  }
  
  /* 例 2 */
  @scope (.card) {
    > img { /* 匹配 .card > img 样式 */ }
    & > img { /* 匹配 .card > img 样式 */ }
    :scope > img { /* 匹配 .card > img 样式 */ }
  }
  
  /* 例 3 */
  @scope (.card) to (.content) {
    img { /* 匹配 .card 到 .content 之间 img 样式, 不包含 img.content */ }  
  }
  
  /* 例 4 */
  @scope (.card) to (:scope > .content) {
    img { /* 匹配 .card img, 但是不匹配 .card > .content img */ }  
  }
  
  • <scope-start> 是一个 CSS 选择器,用于标识作用域根节点
  • <scope-end> 是一个 CSS 选择器,用于标识作用域限制元素
  • <rule-list> 表示作用域声明的样式

注意事项:

在上面 例子2:scope& 有着相同的作用,都匹配了 .card > img 的样式,但其实两者之间还是有很大的区别:

  • :scope 表示匹配的作用域根节点,而 & 则表示匹配作用域根节点的选择器 (可重复使用)。

    css 复制代码
     @scope (.card) {
      :scope :scope { 
        /* ❌ 不能工作 */ 
      }
      
       & > & { 
         /* 匹配 .card > .card 样式 */  
       }
     }
     
  • 它们还有一个区别,是在参与 CSS优先级权重计算时,表现不一样,具体会在下个小节 优先级权重 中进行说明。

优先级权重

@scope 本身不会影响 CSS 优先级权重计算,但是 :scope& 对 CSS优先级有着不同的权重

  • @scope
css 复制代码
  @scope (#card) {
    img { /* Specificity = (0,0,1) --- img 为 0,0,1 */ }
  }

  /* 就优先级权重而言,相当于 :where(#card) img */
  • :scope
css 复制代码
  @scope (#card) {
    :scope img { /* Specificity = (0,1,1) --- :scope 为 0,1,0; img 为 0,0,1 */ }
  }
  • &
css 复制代码
  @scope (#card) {
    & img { /* Specificity = (1,0,1) --- & 为 1,0,0; img 为 0,0,1 */ }
  }

  /* 就优先级权重而言,相当于 :is(#card) img */

应用场景范例

  • @scope (.card) { /* ... */ }

    css 复制代码
      @scope (.card) {
        img {
          border-color: green;
        }
      }
  • @scope (.card) to (.card__content) { /* ... */ }

    css 复制代码
      @scope (.card) to (.card__content) {
        img {
          border-color: green;
        }
      }
  • @scope { /* ... */ }

    html 复制代码
      <div class="card">
        <div class="card__header">
          <style>
            @scope {
              img {
                border-color: green;
              }
            }
          </style>
          <h1>Card Title</h1>
          <img src="..." height="32" class="hero">
        </div>
        <div class="card__content">
          <p><img src="..." height="32"></p>
        </div>
      </div>

浏览器兼容性

目前只有谷歌浏览器 (v118+) 支持了 CSS @scope 特性。不过我相信在不久的将来,主流浏览器会对其有更好的支持度。

相关推荐
鹏多多6 分钟前
纯前端人脸识别利器:face-api.js手把手深入解析教学
前端·javascript·人工智能
无奈何杨44 分钟前
CoolGuard增加枚举字段支持,条件编辑优化,展望指标取值不同
前端·后端
掘金安东尼1 小时前
工具过多:如何管理前端工具泛滥?
前端
江城开朗的豌豆1 小时前
从生命周期到useEffect:我的React函数组件进化之旅
前端·javascript·react.js
brzhang1 小时前
当AI接管80%的执行,你“不可替代”的价值,藏在这20%里
前端·后端·架构
江城开朗的豌豆1 小时前
React组件传值:轻松掌握React组件通信秘籍
前端·javascript·react.js
Sailing1 小时前
别再放任用户乱填 IP 了!一套前端 IP 与 CIDR 校验的高效方案
前端·javascript·面试
程序员爱钓鱼4 小时前
Go语言实战案例 — 项目实战篇:简易博客系统(支持评论)
前端·后端·go
excel11 小时前
ES6 中函数的双重调用方式:fn() 与 fn\...``
前端
可乐爱宅着11 小时前
全栈框架next.js入手指南
前端·next.js