測試瀏覽器原生 CSS Nesting 能不能完全做到 SCSS Nesting 的功能

巢狀CSS (CSS nesting)終於從 Chrome 版本號 120 開始支援,去年(2023/8左右) Firefox 就支援 CSS nesting 了,以前寫 SCSS 的老人們可以放心直接寫,不用再編譯嗎? 並沒有,本文就來做一些實測。

巢狀結構寫法,有時候可以提升 CSS 樣式碼的易讀性和可維護性,如下圖所示,

左邊是 SCSS,右邊是編譯出來給瀏覽器看的 CSS。
人類也可以直接寫出右邊的 code,但是要多打了一大堆重複的部份,
反觀左邊的看起來輕便不少。
如果是用程式碼字數來算錢的,那左邊就是讓國家 GDP 和稅收大幅下降的罪人,
如果是依照檔案數量來算錢,那一個專案中多出一倍的檔案(原始檔和編譯後的檔案),還有一堆 npm 工具產生的檔案和資料夾,大家都將成為新的富翁!

巢狀 CSS 寫法很舒服,但是以前的網頁瀏覽器都看不懂,
開發者必須要用 SCSS/SASS 這種預處理器(preprocessors) 把 CSS 先編譯過,樣式寫得好不好先不論,整個編譯 CSS/JS 相關 toolchain 的演進又是一串辛酸史。
現在瀏覽器直接內建支援了!
距離工具自由可能又更近了一步。

醜話先講在前頭,蘋果大魔王 iOS16.4 以下都不支援 瀏覽器原生的 CSS nesting,開發者可能要兩三年後 iOS 20 公開發布,iPhone 出到 19 了,大家把 iOS 作業系統都升級上來了,才敢放心在專案中使用。

不過還是值得先試試,
至少看看瀏覽器原生的,能不能做出跟 SCSS Nesting 一模一樣的功能?
直接上範例樣式,這樣每次回來看,就知道支援度到哪邊…

把 media query 放進去

(可以拉動邊框右下角來模擬不同的視窗大小)

 .test1{
        padding:5px 10px;
        background:#ccc;
        margin:0 auto 10px;
        max-width:1000px;
        width:100%;
        @media (orientation:landscape){
            background:#000;
        }
        .landscape{color:#ccc}
        &:after{
            content:'';
            display: block;
            width: 100%;
            background: yellow;
            margin-top: 10px;
            @media (max-width:300px){
                background:brown;color:#fff;
            }
        }
 
    }

會發現沒有預期變成咖啡色,因為包在**偽元素裡面的 media query **沒有生效,但是如果用 SCSS 編譯出來是正常的。
網路上查到說是 W3C 的規格 The nesting selector cannot represent pseudo-elements
所以會用偽元素的可能要小心…

多層多組 selector 與各種 combinators

刻意模擬多包幾層的方式,看看是否有不能被包在巢狀裡面的?

.test2{
  .alert, .warning {
    ul, p {
      background:red;color:#fff;margin:0;
    }
    ul{
      list-style:none;padding:0;margin:0;
      ~ p{background:#229241;color:#fff}
      + p{background:#0066cc;color:#fff}
      >li:nth-child(1){
        background:#000;
        &:before{content:'要顯示偽元素';display:inline;color:#ccc}
        &:after{content:'游標移入會變黃色背景';display:inline;color:#ccc}
        &:hover{background:yellow}
      }
    }
  }
}

在巢狀中用上了 nth-child, :hover, ~, +,至少包了兩三層,一切看起來都還正常。

has 選擇器、屬性選擇器和其他

 .test3{
  *{background:#0066cc;color:#fff;list-style:none;padding:0;margin:0}
  li:has(span) {background:#229241}
  li:has(input[type=checkbox]) {background:red}
  #test3{background:yellow;color:#000}
}

在巢狀中選到了 ID,另外還用了 has 選擇器來選 tag 或特定屬性的 input,也都能正常吃到樣式。

CSS 變數與 calc

.test4 {
     width: 200px;
     aspect-ratio: 1;
     --corner1:30%;
     --corner2:70%;
     clip-path: polygon(
      var(--corner1) 0%,
      var(--corner2) 0%,
      100% var(--corner1),
      100% var(--corner2),
      var(--corner2) 100%,
      var(--corner1) 100%,
      0% var(--corner2),
      0% var(--corner1)
     );
 
     background: linear-gradient(to right,#5ec7c1,#20e3b2,#0cebeb);
     display:flex;align-items:center;justify-content:center;
     span{background:#fff;aspect-ratio: 1;display:block;width:calc(var(--corner2) - var(--corner1))}
}

把 CSS 變數寫在巢狀中,還用 calc 來作減法運算,看起來也都正常。
ps.如果要畫八角型還可以用 css 的三角函數,用了 tan 之後就只需要一個變數,可參考 Temani Afif 的範例

巢狀簡寫

碰到 CSS 屬性或是 class name 開頭一樣的,在 SCSS/SASS 還有 Nested Properties 之類的特殊寫法可以用。
例如 background-color, background-repeat…系列的,或 font-weight, font-size… 系列的 CSS 屬性,
或是 UI 元件的內部零件,命名規則像是 .card-body, .card-footer 這種,
一養的東西只要打一次,用巢狀結構包好,編譯後會把省略的地方通通補好。

來測試瀏覽器原生的巢狀 CSS 是否支援這種寫法。

        .test5{
            font: {
                family: 'Noto Sans TC', sans-serif;
                size: 18px;
                weight: 600;
            }
            [class^="card-"] {padding: 5px 10px;color:#fff}
            .card {
                background: #f4f4f4;
                &-head {background: green;}
                &-body {background: #000;}
                &-footer {background: #0066cc;}
            }
        }

結果看起來目前是不支援,可惜。

其他

蒐集一些網路上看到的 CSS nesting 有趣用法。

& 在 CSS 與 SCSS 的差異

看不到貼文此點此 T. Afif @ CSS Challenges
比較使用了 .main &{} 在 CSS 與 SCSS 的差異。


看不到貼文此點此 Lea Verou – Realization: CSS Nesting also allows you to basically do “else” clauses in selectors.

用 not 來達成類似 if-else 的效果

a, abbr, acronym, b, bdo, big, br, cite, code, dfn, em, i, kbd, label, mark,
output, samp, small, span, strong,  sub, sup, time, tt, var {
    outline: 1px dashed hsl(220 10% 50% / 50%) !important;
    :not(&) {
        outline: 1px dashed red !important;
    }
}

這種用法使用了 :not(&) ,就能達成類似 if-else 的目標,除了某些元素之外,通通套用另一組樣式。

心得

導入瀏覽器原生 CSS Nesting 的主要缺點有3個:

  • 瀏覽器相容性問題
    必須要等到使用者都升級到 iOS16.4 以上,才支援瀏覽器原生的 CSS Nesting,在此之前都難以避免有人打開網頁後無法正常顯示的問題。
    這段時間是漫長的,就連在 2024 年用 `aspect-radio`,常常被沒更新手機的使用者雷到。
    使用者看到網頁樣式不正常就打來狗幹窗口,不知道是自己的手機沒升級,這樣還有開發人員敢用嗎?
    你說合約上有規範瀏覽器版本? 主管或老闆可能才不管那些,只問你什麼時候可以把網頁修好?
  • 有老專案要維護的,或是以前習慣用 SCSS 的,光是 SCSS/SASS 常用的 Nesting 寫法,目前瀏覽器原生的 CSS Nesting 還無法完全做到,就更別提 SCSS/SASS 現有的其他功能了。
  • 有些線上或本機的程式碼編輯器,或是在網頁上幫程式碼加 Syntax highlighting 的套件,都需要更新。
    不然碰到這種比較新的寫法,會顯示像是打錯字一樣的提示,或是變色變得怪怪的,或是每打一行按 Enter 就亂補 } 括號,用起來都會有點令人煩躁喔。

這功能也有點姍姍來遲,遲到甚至調樣式都不一定需要寫 CSS 了,
可能用 TailwindCSS 這種原子式 CSS,
或是一些設計工具產出 code 再修,
或是用 No Code 工具在密密麻麻的屬性視窗中選擇樣式數值之類的,
但這些方式不一定適用所有工作流程,可能還有一堆現有用 SCSS 刻樣式的網站要維護。

最後又想到還有一種需要寫原生 CSS 的場合,就是排 EDM !
那各種常見的 email client 是否支援 CSS Nesting?
根據 Can I email – CSS Nesting的資料,Gmail 和 Outlook 都是滿江紅,所以還是乖乖寫普通的 CSS 吧。

Tags: #css#css3
分類: 網頁設計
留言:

近期熱門 Hot Posts