Sass 與原生 巢狀結構

Natalie Weizenbaum 發佈於 2023 年 3 月 29 日

今天發布的 Chrome 112 穩定版是第一個新增支援 原生 CSS 巢狀結構功能 的穩定版瀏覽器。這個功能的靈感來自 Sass 的巢狀結構,它增加了在純 CSS 中巢狀樣式規則的功能,甚至使用了 Sass 的 & 慣例來引用父 選取器。

我們 Sass 總部每次看到我們的語言設計啟發了 CSS 本身的改進都感到非常榮幸。隨著越來越多的瀏覽器持續推出對此功能的支援,我們很高興看到巢狀結構的易用性和清晰度能讓更多 CSS 作者 受益。

Sass 巢狀結構的未來Sass 巢狀結構的未來永久 連結

然而,這也引出了一個重要的問題:Sass 的巢狀結構將會如何?首先,我們永遠不會更改現有的有效 Sass 程式碼,使其開始產生與廣泛使用的瀏覽器不相容的 CSS。這意味著,即使我們決定逐步淘汰 Sass 巢狀結構,並改用純 CSS 巢狀結構,我們也不會在 全球 98% 的瀏覽器市佔率 支援原生巢狀結構之前這樣 做。

更重要的是,原生 CSS 巢狀結構與 Sass 巢狀結構存在細微的不相容性。這會影響三個不同的 情況:

  1. 原生 CSS 巢狀結構會將父選取器隱式包裝在 :is() 中,而 Sass 則將其文字複製到解析後的選取器中。這意味著 

    .foo, #bar {
      .baz { /* ... */ }
    }

    在 Sass 中會產生選取器 .foo .baz, #bar .baz,但在原生 CSS 中會產生 :is(.foo, #bar) .baz。這改變了特異性::is() 的特異性始終與其*最具特異性的選取器*相同,因此 :is(.foo, #bar) .baz 將會 匹配…

    <div class=foo>
      <p class=baz>
    </div>

    在原生 CSS 中的特異性為 1 0 1,在 Sass 中的特異性為 0 0 2,即使兩個元素都沒有被 ID 匹配。

  2. 同樣,由於原生 CSS 巢狀結構使用 :is(),因此具有後代組合器的父選取器的行為也會 不同。

    .foo .bar {
      .green-theme & { /* ... */ }
    }

    在 Sass 中會產生選取器 .green-theme .foo .bar,但在原生 CSS 中會產生 .green-theme :is(.foo .bar)。這意味著原生 CSS 版本將會 匹配…

    <div class=foo>
      <div class="green-theme">
        <p class=bar>
      </div>
    </div>

    但 Sass 不會匹配,因為匹配 .foo 的元素在匹配 .green-theme 的元素 之外。

  3. Sass 巢狀結構和原生 CSS 巢狀結構都支援看起來像 &foo 的語法,但它們的含義不同。在 Sass 中,這是將後綴添加到父選取器 中,因此…

    .foo {
      &-suffix { /* ... */ }
    }

    會產生選取器 .foo-suffix。但在原生 CSS 中,這是將類型選取器添加到父選取器 中,因此…

    .foo {
      &div { /* ... */ }
    }

    會產生選取器 div.foo(而 Sass 會產生 .foodiv)。原生 CSS 巢狀結構無法像 Sass 那樣向選取器添加 後綴。

設計承諾設計承諾永久連結

在考慮如何處理這個新的 CSS 功能時,我們有兩個重要的設計承諾需要牢記 在心:

  • 我們致力於成為 CSS 的超集。所有在實際瀏覽器中支援的有效 CSS 也應該能在 Sass 中以相同的語義運作。

  • 我們致力於向後相容。我們盡可能避免更改現有樣式表的語義,如果必須這樣做,我們會盡可能提供使用者足夠的時間和資源來順利完成更改。

在大多數情況下,維持 CSS 超集的地位比向後相容性更重要。然而,巢狀結構是 Sass 最古老且使用最廣泛的功能之一,因此我們尤其不願意更改它,尤其是以會捨棄廣泛使用的功能(例如 `&-suffix`,在原生 CSS 中沒有優雅的對應功能)的方式進行更改。

Sass 的計畫Sass 的計畫 永久連結

短期內,我們不打算更改 Sass 巢狀結構的任何內容。除非我們能以完全相容現有 Sass 行為的方式進行,否則 Sass 將不會支援純 CSS 巢狀結構。

我們新增支援在 `.css` 檔案中解析純 CSS 巢狀結構。這個巢狀結構不會以任何方式解析;Sass 只會照原樣輸出它。

長期而言,一旦 :is() 被全球 98% 的瀏覽器市佔率支援後,我們將開始轉換 Sass,在解析 Sass 巢狀結構時輸出 :is()。這將使 Sass 在前兩個行為不相容性方面與 CSS 的行為相同。我們將此視為重大變更,並將其作為主要版本的一部分發布,以避免意外破壞現有的樣式表。我們將盡最大努力使用 Sass Migrator 使轉換過程盡可能順暢。

除非我們能想出一個更符合 CSS 且同樣簡潔的方式來表示它,否則我們不會捨棄目前 &-suffix 的行為。此行為對於現有的 Sass 使用者來說太重要了,而純 CSS 版本的優勢不足以凌駕於此。