測試瀏覽器原生 CSS Nesting 能不能完全做到 SCSS Nesting 的功能
巢狀結構寫法,有時候可以提升 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 的差異
CSS Nesting is cool and using the "&" is a lifesaver BUT be careful. There's a gotcha!
⚠️ "&" in CSS is different from the one in Sass ⚠️
Your Sass code won't work the same way if you use it as a CSS code.
Yes, it's counter-intuitive but keep reading to understand why 👇 pic.twitter.com/qK1xBCFD3V
— T. Afif @ CSS Challenges (@ChallengesCss) February 19, 2024
看不到貼文此點此 T. Afif @ CSS Challenges。
比較使用了 .main &{}
在 CSS 與 SCSS 的差異。
Realization: CSS Nesting also allows you to basically do "else" clauses in selectors.
complex-selector {
if-styles;:not(&) {
else-styles
}
}(if you’re wondering what this code is for, it’s for a bookmarklet to show element boxes for educational reasons) pic.twitter.com/zpZa6u4Xvt
— Lea Verou (@LeaVerou) February 12, 2024
看不到貼文此點此 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 吧。