Skip to content

Sass 巢狀規則:依結構縮排書寫,與過深的代價

Sass 巢狀規則的寫法與限制

巢狀(Nesting)是最直覺、也最容易被濫用的 Sass 功能。它讓選取器可以依照 HTML 的結構層層寫進去,不必每一條規則都重複前綴。

選取器巢狀

scss
.card {
  padding: 16px;

  .title {
    font-size: 20px;
  }

  .content {
    color: #666;
  }
}

編譯後會攤平成 後代選取器

css
.card {
  padding: 16px;
}
.card .title {
  font-size: 20px;
}
.card .content {
  color: #666;
}

關鍵在「攤平」這兩個字。 縮排只是書寫方式,產出的仍是一條條完整的選取器,你寫了幾層,選取器就有多長。

巢狀裡的其他選取器

巢狀的內層不一定要是後代,直屬後代同層 都可以:

scss
.list {
  > li {
    padding: 8px 0;
  }

  + .list {
    margin-top: 24px;
  }
}
css
.list > li {
  padding: 8px 0;
}
.list + .list {
  margin-top: 24px;
}

至於偽類、修飾類別這種要「接在父選取器後面、中間不能有空白」的情況,得用 &,見 父選取器

媒體查詢的巢狀

@media 寫在規則內部,Sass 會自動把它提到外層:

scss
.card {
  padding: 16px;

  @media (min-width: 768px) {
    padding: 32px;
  }
}
css
.card {
  padding: 16px;
}
@media (min-width: 768px) {
  .card {
    padding: 32px;
  }
}

這是巢狀最有價值的用途

斷點樣式就寫在它修改的那個屬性旁邊,改一個元件不必在檔案的兩端來回跳。斷點值集中管理的做法見 斷點管理

屬性巢狀

有共同前綴的屬性也能巢狀:

scss
.title {
  font: {
    family: "Noto Sans TC", sans-serif;
    size: 20px;
    weight: 700;
  }
}
css
.title {
  font-family: "Noto Sans TC", sans-serif;
  font-size: 20px;
  font-weight: 700;
}

注意 font: 後面的冒號不能省。要同時給簡寫與細項的話:

scss
.box {
  margin: auto {
    bottom: 16px;
  }
}

這個功能實務上很少用

省下的字不多,可讀性反而下降,而且搜尋 font-size 時找不到它。知道有這回事就好。

三層是那條線

巢狀的代價全在編譯後的產出上:

scss
.page {
  .sidebar {
    .menu {
      .item {
        .link { color: #333; }
      }
    }
  }
}
css
.page .sidebar .menu .item .link {
  color: #333;
}

這一條規則有三個問題:

問題說明
優先權 過高五個類別 = 0,5,0,想覆蓋它得寫更長的選取器
檔案變大每條規則都帶著完整祖先路徑
綁死結構HTML 少一層 <div>,樣式就失效

三層規則

巢狀不要超過三層。 超過的話多半代表命名方式該改了,用 BEM 把結構寫進類別名,配上 &,選取器可以始終維持一層。

改寫成 BEM:

scss
.menu {
  &__item { /* ... */ }
  &__link { color: #333; }
}
css
.menu__item { /* ... */ }
.menu__link { color: #333; }

優先權只有 0,1,0,也不再依賴 DOM 有幾層。

常見問題

巢狀最多可以幾層?

語法上沒有限制,但實務上建議不超過三層。每多一層,產出的選取器就多一段、優先權 就更高,之後要覆蓋它就得寫出更長的選取器,最後演變成到處加 !important

巢狀會讓 CSS 檔案變大嗎?

會。巢狀在編譯時是攤平成完整選取器,三層巢狀的每一條規則都帶著完整的祖先路徑,重複的前綴字串會實際佔用檔案體積,也讓瀏覽器比對選取器的成本上升。

媒體查詢可以寫在巢狀裡嗎?

可以,而且很推薦。把 @media 寫在元件的規則內部,Sass 編譯時會自動把它提到外層並補上完整選取器,好處是斷點的樣式就寫在它修改的那個屬性旁邊,不必在檔案兩端來回對照。

屬性巢狀是什麼?

有共同前綴的屬性可以用巢狀寫,例如把 font-familyfont-size 寫成 font: 加一個巢狀區塊。它能減少重複,但可讀性見仁見智,實務上使用得不多。

延伸閱讀