CSS中@scope的简介

315 阅读3分钟

基于邻近性的样式

现在我们不仅仅依靠源代码的顺序和特异性来确定样式,还可以根据元素的邻近性来覆盖样式。请猜测以下示例中按钮的颜色会是什么:

 @scope (.blue) {
     button {
         background-color: blue;
     }
 }

 @scope (.green) {
     button {
       background-color: green;
     }
 }

 @scope (.red) {
     button {
       background-color: red;
     }
 }

示例 1:

 <div class="red">
     <div class="green">
         <div class="blue">
             <button>Click</button>
         </div>
     </div>
 </div>

示例 2:

 <div class="blue">
     <div class="green">
         <div class="red">
             <button>Click</button>
         </div>
     </div>
 </div>

答案:只需看最近的祖先元素。在示例 1 中,按钮是蓝色的。在示例 2 中,按钮是红色的。如果您正在使用 Chrome Canary,您可以查看 CodePen 示例:codepen.io/cssgrid/pen…

让我们来看一个真实的使用案例。@scope 解决了我在英国电话网络 giffgaff 工作时遇到的问题。我们有所谓的 “主题” - 不是根据用户偏好而改变的浅色和深色主题,而是用特定的颜色方案为页面的不同部分设置样式的类。为了确保足够的颜色对比度以便轻松阅读,链接文本在浅色背景上是深蓝色,在深色背景上是浅蓝色。这样我们就不需要在每个链接上都加上一个类,这样做既繁琐又容易出现不一致性。

.theme-white {
   background-color: white;
   color: black;
   }

 .theme-white a {
   color: #00528a;
 }

 .theme-black {
   background-color: black;
   color: white;
 }

 .theme-black a {
   color: #35adce;
 }

这种方法运作得还不错,但存在一个问题:嵌套。CSS 并不根据最近的 HTML 祖先元素来确定应用哪种样式,它只按照 CSS 文件中的源代码顺序进行处理。根据您定义样式的顺序,如果您将一个白色区块嵌套在一个黑色区块中,或者将一个黑色区块嵌套在一个白色区块中,链接的颜色将不再正确。在 @scope 出现之前,实际上没有解决这个问题的方法。

 <div class="theme-white">
   <a href="example.com">This link is the correct color</a>
 </div>

 <div class="theme-black">
   <a href="example.com">This link is the correct color</a>

   <div class="theme-white">
     <a href="example.com">This link is the wrong color</a>
   </div>

 </div>

image.png

使用 @scope,我们可以解决这个问题:

 .theme-white {
    background-color: white;
    color: black;
  }

 .theme-gray {
    background-color: #f5f5f5;
    color: black;
  }

 @scope (.theme-white, .theme-gray) {
   a {
     color: #00528a;
   }
 }

 .theme-black {
   background-color: black;
   color: white;
 }

 @scope (.theme-black) {
   a {
     color: #35adce;
   }
 }

现在,无论链接是在白色或灰色背景上,它都会显示为深蓝色。而在黑色背景上,它会显示为更亮的浅蓝色。

我们可以选择重写之前的 CSS,使用:scope 伪类来引用当前作用域的根元素。在下面的示例中,:scope 将选择具有.theme-black 类的任何元素。

 @scope (.theme-black) {
    :scope {
      background-color: black;
      color: white;
    }
    a {
      color: #35adce;
    }
 }

:scope 并不是一个新的伪类。它在浏览器中已经存在多年,但在样式表中使用时通常没有实际意义,因为在 @scope 块之外,它始终与:root 相同(:root 选择文档的根元素,即 <html> 元素)。

为选择器设置较低的边界

有时候你想为一个组件设置样式,但又不想为其中嵌套的某些内容设置样式。

作用域规范的共同作者 Miriam Suzanne,几年前在 Syntax 播客中谈到了 @scope:“无论你把内容放在标签页的哪个地方,标签页组件都有这些空白。不希望 tab 组件样式化内容,只想样式化选项卡... 我们有后代选择器,你可以在选项卡内说任何东西,但这不是我们想要的。我们希望在达到选项卡内容之前,选项卡内的任何东西都能被选择。希望能够在选择器上设置一个较低的边界。”

让我们来看一下语法:

 @scope (.component) to (.content) {
   p {
     color: red;
   }
 }

第二个选择器设置了一个较低的边界,即从这个点停止样式化。

 <div class="component">

   <p>In scope.</p>

   <div class="content">
     <p>Out of scope.</p>
   </div>

 </div>

如果在.content 内部有一个段落(<p>),它将不会被选择(如果您的浏览器支持 @scope,您可以查看 CodePen 的示例:codepen.io/cssgrid/pen…

一个 @scope 可以有任意多个 “空洞”(holes):

 @scope (.component) to (.content, .slot, .child-component) {
   p {
     color: red;
   }
 }

进一步阅读:

您可以阅读《级联和继承级别 6 规范》中关于作用域的更多内容。