CSS 範疇控制 (CSS Scope)

@scope 是 CSS 的一項新特性,它允許開發者將樣式明確地限制在文件中的特定區域內。這解決了長期以來 CSS 樣式容易「溢出 (Leak)」到其他組件的問題,而不再需要完全依賴 BEM 或 CSS Modules 等外部工具。

什麼是 @scope?

傳統 CSS 是全域的,除非你使用非常具體的選擇器。@scope 則讓你宣告:「這些樣式只對在這個元素內部的東西生效」。

@scope (.card) {
  /* 這裡的 img 只會影響 .card 內部的圖片 */
  img {
    border-radius: 8px;
  }

  .title {
    font-size: 1.25rem;
  }
}

對應的 HTML:

<div class="card">
  <img src="photo.jpg" />
  <h2 class="title">卡片標題</h2>
  <p>這張圖片會有圓角,且標題顏色會變動。</p>
</div>

<!-- 外部的圖片不會受到影響 -->
<img src="other.jpg" />

甜甜圈範疇 (Donut Scoping)

@scope 最強大的地方在於「範圍限制(Scoping Limit)」,這被暱稱為「甜甜圈範疇」。你可以定義一個範圍的起點,並排除特定子區域的樣式。

/* 起點是 .card,但當遇到 .content 時停止作用 */
@scope (.card) to (.content) {
  img {
    border: 5px solid red;
  }
}

對應的 HTML:

<div class="card">
  <img src="outer.jpg" />
  <!-- 會套用紅框 -->

  <div class="content">
    <img src="inner.jpg" />
    <!-- 不會套用紅框,因為進入了排除範圍 -->
  </div>
</div>

這在處理「卡片中嵌入了另一個獨立組件」的場景非常有用,避免父組件的樣式意外影響到子組件。

特殊符號 :scope

在使用 @scope 時,:scope 偽類代表的是目前範圍的「根元素」。

@scope (.article) {
  /* 這裡的 :scope 指的就是 .article 元素本身 */
  :scope {
    padding: 2rem;
    background: #f9f9f9;
  }

  p {
    color: #333;
  }
}

對應的 HTML:

<div class="article">
  <p>這個 div 本身會有背景色與內距,內部的文字則是深灰色。</p>
</div>

為什麼要使用 @scope?

  • 更乾淨的選擇器:不需要撰寫長長的 .navbar .nav-list .nav-item a,直接在 @scope (.navbar) 內撰寫 a 即可。
  • 權重管理:在 @scope 內定義的樣式,其優先權計算方式能更好地與 DOM 結構吻合,減少樣式衝突。
  • 原生封裝:接近 Shadow DOM 的行為,但更輕量且不需要 JavaScript。

實務範例

@scope (.sidebar) {
  /* 僅作用於側邊欄內的連結 */
  a {
    display: block;
    color: gray;

    &:hover {
      color: black;
    }
  }
}

對應的 HTML:

<aside class="sidebar">
  <nav>
    <a href="#">首頁</a>
    <a href="#">關於我們</a>
  </nav>
</aside>

瀏覽器支援度

@scope 目前已在 Chrome 118+ 與 Safari 17.4+ 中正式支援。雖然 Firefox 目前(2025 年初)仍在開發階段,但這項功能已被視為現代組件化開發的核心組成部分。

如果你正在開發中大型專案或使用 React/Vue 等組件化框架,@scope 是除了 CSS Modules 之外另一個非常值得考慮的原生樣式封裝工具。