2021年8月29日 星期日

SCSS / SASS筆記 @extend 與 %style

使用 SCSS 工作時的筆記。用點力統整、擴寫一下,內容已經有點太長,囧。

@extend 與 %style

官方文件 https://sass-lang.com/documentation/at-rules/extend
功能:引用對象 .classB 與既有樣式 .classA (此稱拓展對象) 共用 classA 的樣式,形成 .classA, .classB{...(classA的css宣告)...} 這般的grouping selector;此語法拓展 grouping selectors 的應用彈性。基礎用法請參照官方範例,筆記內文沒有著墨。
  1. 拓展對象需比引用對象提前宣告,以方便後者引用
  2. class name 不能複數引用
  3. 根據官網所言,若 selector .A.B{...} 存在,可成功將 style 拓展到 .A.B 之對應樣式
    @extend .A.B;
  4.  class name 是可以加變數的。但記得 mixin name 中則不行加變數的樣子。
    @extend .style-#{$var};
  5. %style{...} 不會被編譯,但可被@extend 引用
    @extend %style;
@extend 與 @media require 混用注意事項
  • bootstrap框架使用者注意: @media require 等同於 @include media-breakpoint-*() in bootstrap。media-breakpoint-*() 內混用 @extend 時也要一併考量下文提到的狀況, SCSS 比較能順利編譯成功。
SCSS 編譯時,會
  1. 步驟一:先檢查引用對象 .classB 是否存在 media require 
  2. 步驟二:再對比拓展對象 .classA 是否與引用對象 .classB 擁有相同 media require ,若兩者 @media 條件式不同,則會導致編譯失敗。
.classA{ background:pink; } @media (min-width:576px){ .classB{ border-color:gray; @extend .classA; // 無法成功編譯 } }

編譯失敗原因在於:@extend .classA 本質上的功能是將引用對象 .classB 加個逗點掛在 .classA 後面,變成 grouping selects,成為 .classA, .classB{...(classA的css宣告)..}。但在使用 @media 時,media require 將優先於 class 宣告,因此 .classB{ ... } 外面會包覆一層 media require,形成 @media (...){ .classB{...}}。多了一層包覆結構的 @media (...){ .classB{...}},難以和 .classA 形成合法的 grouping selects 。例如 .classA,@media (...){ .classB}{...(classA的css宣告)..} 是一個不合法的選擇器(selector),因此 SCSS 編譯器會提前檢查出錯誤並阻擋之後的編譯過程。
.classA, @media (min-width:576px){.classB}{ //不合法的selector background:pink; }
@extend 與 @media 混用之解法
  1. 宣告一個和 .classB 之 media require 條件相同的 .classA
    1. 直接替拓展對象 .classA 包覆條件一樣的 media require。
      但此舉需注意其他有 extend .classA 的 class 是否也符合相同media require,否則依然導致編譯失敗
    2. 複製一份 .classA 再將它包覆條件一樣的 media require。
    3. 將拓展對象 .classA 重新在 包含有目的 .classB 的 media require 裡再宣告一次(一樣是再複製一次的概念)
  2. 把 .classA 內部 css 屬性取出命名為新mixin,即能以引用mixin的方式,無視 media require 隨意引用在各種class的肚子裡

.classA{ background:pink; } @media (min-width:576px){ .classA-1-2 { // 方法1.2; color:green; } } @media (min-width:576px){ .classA-1-3{ // 方法1.3 margin:1rem; } .classB{ border-color:gray; @extend .classA-1-2; @extend .classA-1-3; } } .classB-in-bootstrap{ // 將示範目標改寫為 bootstrap 常見寫法 border-color:gray; @include media-breakpoint-up(sm){ //@extend .classA;  // 依然無法成功編譯 @extend .classA-1-2; @extend .classA-1-3; } }

方法2. mixin作法範例,等mixin的相關筆記補完後再寫。

同是media require 但倒過來相反
和第一個範例情況相反的例子:拓展對象 .classC 具有 media require 限制,但引用對象 .classD 沒有media require,此情況卻能夠成功編譯。
@media (min-width:576px){ .classC{ font-size:3rem; } } .classD{ @extend .classC; // 可以成功編譯 }

先前提過,因為編譯時會先檢查「步驟一:引用對象是否具有 media require」再進行其後步驟。既然引用對象沒有 media require,就不需再往下檢查其他錯誤可能,直接編譯成功。為何這樣可以行得通的原因,一樣要回歸 @extend 的原始原理:引用對象 .classD 易與拓展對象 @media (...){.classC{...}} 形成 grouping selectors,即:
@media (...){ .classC, .classD{ ... } }

延伸:bootstrap 框架已先備許多 breakpiont classes,就很適合在 bootstrap 後的客製化 css/scss 內隨手引用。

沒有留言:

張貼留言