漫談 CSS 方法論

語言: CN / TW / HK

大家好,我是 CUGGZ。

CSS 方法論是一種面向 CSS、由個人和組織設計、已被諸多專案檢驗且公認有效的最佳實踐。這些方法論都會涉及結構化的命名約定,並且在組織 CSS 時可提供相應的指南,從而提升程式碼的效能、可讀性以及可維護性。

根據 State of CSS  2020 的調查結果顯示,目前使用最多的五種分別為:BEM、ACSS、OOCSS、SMACSS、ITCSS。

下面就分別看看這五種 CSS 方法論!

1. BEM

BEM 全稱為 Block Element Modifier,分別表示塊(Block)、元素(Element)、修飾符(Modifier),它是由 Yandex 團隊提出的一種 CSS 命名方法。這種命名方法讓 CSS 便於統一團隊開發規範和方便維護。該方法論由以下三部分組成:

  • Block:儘量以元素的性質來命名物件,例如:.list、.card、.navbar;
  • Element:使用__​ 兩個下劃線來連線 Block 物件,例如:.list__item、.card__img、.navbar__brand;
  • Modifier:使用--​ 兩個連字元連線 Block 或 Element 物件,例如:.list__item--active、.card__img--rounded、.navbar--dark。

在 BEM 中並沒有那些抽象的規則,它是基於功能為導向(Function-Oriented Programming, FOP)而設計的,不存在像是 .pl-3 這種難以理解的 class 名稱,為了保證 BEM 能夠合理的將元素模組化,需要遵守以下規則:

  • 不能使用class 以外的選擇器來編寫樣式;
  • 不要過度模組化,應該適當控制元素的模組化深度。

(1)Block 塊

所謂的 Block 就是指應用中可獨立存在的元素,類似於 SMACSS 中的 Layout 或 Module,這裡稱其為塊。需要遵守以下規範:

  • Block 名稱需要清楚的表達其用途、功能、意義,且具有唯一性;
  • Block 可以放在頁面上的任何位置,也可以相互巢狀;
  • 單詞之間可以使用駝峰形式或者使用- 將其分隔。

參考以下程式碼:

.list { /* ... */ }

.card { /* ... */ }

.navbar { /* ... */ }

.header { /* ... */ }

(2)Element 元素

如果把塊描述為一個元素,那就可以將 Element 描述為此元素的子元素。參考以下規則:

  • Element 名稱需要清楚的表達元素的用途及意義;
  • Element 和 Element 之間可以相互巢狀;
  • Element 與Block 之間使用__ 兩個下劃線連線;
  • 單詞之間可以使用駝峰式或者使用- 將其分隔。

參考以下程式碼:

.list__item { /* ... */ }

.card__img { /* ... */ }

.navbar__brand { /* ... */ }

.header__title { /* ... */ }

這裡需要注意,Element 無法獨立於 Block 之外,其存在的目的就是子元素,元素既然不存在,那何來的子元素?如果使用 SCSS 來編寫樣式,可以改用 & 父選擇器來:

.list {
  display: flex;

  &__item {
    flex: 0 0 25%;
  }
}

這樣可讀性就變好了一些,然後就可以將這些樣式應用於 HTML 元素上了:

<ul class="list">
  <li class="list__item"></li>
  <li class="list__item"></li>
  <li class="list__item"></li>
  <li class="list__item"></li>
</ul>

BEM 沒有過多複雜的概念,而且通過 class 名稱就可以知道 HTML 元素的結構,更容易理解和使用。

下面再來看一個 Element 相互巢狀的例子:

<ul class="list">
  <li class="list__item">
    <a href="list__item__link"></a>
  </li>
  <li class="list__item">
    <a href="list__item__link"></a>
  </li>
</ul>

一個元素通常不會只有兩層結構,很多時候都具有三層以上的結構。此時使用 BEM 編寫的程式碼就會像上面這樣。這裡的問題在於這樣的處理會造成巢狀越來越深,導致 class 名稱越來越長,HTML程式碼可讀性越來越差。如果存在多層巢狀,可以嘗試進行以下修改:

<ul class="list">
  <li class="list__item">
    <a href="list__link"></a>
  </li>
  <li class="list__item">
    <a href="list__link"></a>
  </li>
</ul>

這也就意味著,所有的子元素都僅僅會被 .list 所影響,link 不會綁死在 item 下,link 可以自由的放在 list 的任何位置。

(3)Modifier 修飾符

Modifier 就像 OOCSS 中的 Skin 與 SMACSS 中的 State,主要用來表示 Block 或 Element 的行為及樣式。參考以下規範:

  • Modifier 名稱需要清楚的表達元素樣式、狀態或行為;
  • Modifier 與 Block 或 Element 之間使用-- 兩個連字元連線;
  • 單詞之間可以使用駝峰式或者使用 - 將其分隔。

參考以下程式碼:

.list__item--active { /* ... */ }

.card__img--rounded { /* ... */ }

.navbar--dark { /* ... */ }

.header__title--size-s { /* ... */ }

Modifier 也是無法單獨存在的,Modifier 必定是作用於某個物件,這裡所指的物件可能是 Block 或 Element。如果使用的是 SCSS,可以改用 &父選擇器:

.list {
  display: flex;

  &__item {
    flex: 0 0 25%;

    &--active {
      color: #fffc3d;
    }
  }

  &--dark {
    color: #fff;
    background-color: #272727;
  }
}

然後就可以將樣式應用在 HTML 元素上了:

<ul class="list list--dark">
  <li class="list__item"></li>
  <li class="list__item list__item--active"></li>
  <li class="list__item"></li>
  <li class="list__item"></li>
</ul>

從上面 HTML 程式碼中就可以很明顯的看到其關聯性,很容易的辨認出哪些是 Block,哪些是 Element,哪些是 Modifier,並進一步推斷出哪部分的 HTML 可以獨立使用,這也是 BEM 的初衷。

2. OOCSS

(1)基本概念

OOCSS 是 Object Oriented CSS 的縮寫,意為面向物件的CSS。它是所有 CSS 方法論中最早提出的一個,由 Nicole Sullivan 提出。可以把它理解為將 CSS 模組化。

OOCSS 提倡樣式可重用性,在編寫 CSS 樣式時需要遵循以下規則:

  • 應儘量避免使用後代選擇器(.navbar ul) 或 id 選擇器(#list);
  • 應儘量避免樣式依賴於結構,嘗試使用class 替代標籤選擇器。

(2)主要原則

OOCSS 有兩個主要原則:結構與樣式分離和容器與內容分離。

OOCSS 作者對這兩個原則的描述

1)結構與樣式分離(Separate structure and skin)

結構與樣式之間就像 .btn 與 .btn-primary 的關係一樣。來看例子:

$theme-colors: (
  primary: blue,
  success: green,
);

.btn-primary {
  display: inline-block;
  padding: 0.375rem 0.75rem;
  color: #fff;
  background-color: map-get($theme-colors, primary);
  border: 1px solid map-get($theme-colors, primary);
  border-radius: 0.25rem;
}

平時我們可能習慣性的把全部樣式都寫在同一個class 中,就像上面程式碼一樣。此時如果我們需要新增一個主題為 success 的按鈕呢?你可能會這樣做:

.btn-primary {
  display: inline-block;
  padding: 0.375rem 0.75rem;
  color: #fff;
  background-color: map-get($theme-colors, primary);
  border: 1px solid map-get($theme-colors, primary);
  border-radius: 0.25rem;
}

.btn-success {
  display: inline-block;
  padding: 0.375rem 0.75rem;
  color: #fff;
  background-color: map-get($theme-colors, success);
  border: 1px solid map-get($theme-colors, success);
  border-radius: 0.25rem;
}

這時,兩個按鈕就會有很多重複的樣式,每增加一個主題的按鈕,就需要增加一組樣式,這就有點浪費時間了。而 OOCSS 中的結構與樣式分離主要就是為了改善這個問題,將以上程式碼根據 OOCSS 的規範進行改寫:

.btn {
  display: inline-block;
  padding: 0.375rem 0.75rem;
  color: black;
  background-color: transparent;
  border: 1px solid transparent;
  border-radius: 0.25rem;
}

.btn-primary {
  color: #fff;
  background-color: map-get($theme-colors, primary);
  border: 1px solid map-get($theme-colors, primary);
}

在 OOCSS 的概念中,表現型的 style 就屬於樣式,封裝型的 style 就屬於結構,如下所示:

  • 樣式(skin):color、background-color、border-color;
  • 結構(structure):display、box-sizing、padding。

那這樣做的用意是什麼呢?button 按鈕一般是這樣來使用這些樣式的:

< button  class = "btn btn-primary" > Primary </ button >

這樣就可以很明確的知道這個元素的結構與樣式,以後如果想要增加不同主題的按鈕,就只需要編寫像.btn-success、.btn-danger這樣的樣式類即可,而無需再編寫按鈕的結構。

當然,也可以藉助 Sass 中的 @each 來更快速地實現多種主題按鈕的樣式:

$theme-colors: (
  primary: blue,
  success: green,
  danger: red,
);

.btn {
  display: inline-block;
  padding: 0.375rem 0.75rem;
  color: black;
  background-color: transparent;
  border: 1px solid transparent;
  border-radius: 0.25rem;
}

@each $key, $value in $theme-colors {
  .btn-#{$key} {
    color: #fff;
    background-color: $value;
    border: 1px solid $value;
  }
}

按鈕在定義時只需新增對應的樣式即可:

button class="btn btn-primary">Primary</button>
<button class="btn btn-success">Success</button>
<button class="btn btn-danger">Danger</button>

相信通過上面的例子,你已經瞭解了什麼是結構與樣式分離。如果以 OOCSS 中的 OO (Object Oriented) 來描述的話,這裡的結構(Structure)就是所指的元素。以上面例子來說,我們封裝了 button 元素,以後如果要使用 button 的話,只需要編寫 .btn 結構樣式名稱與對應的樣式(skin)即可。

2)容器與內容分離(Separate container and content)

接下來看看容器與內容該如何分離。容器與內容之間就像 .card 與 .btn 的關係一樣。來看例子:

.card {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  word-wrap: break-word;
}

.card button {
  display: inline-block;
  padding: 0.375rem 0.75rem;
}

在編寫 CSS 樣式時,通常都是根據 HTML 的結構來編寫。從上面的程式碼中可以看出,.card 裡面有個 button。這樣編寫樣式就會失去靈活度,button 就完全被繫結在了 .card 裡面,OOCSS 中的容器與內容分離主要就是用來改善這個問題的,將上面的程式碼根據 OOCSS 的規範進行改寫:

.card {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  word-wrap: break-word;
}

.btn {
  display: inline-block;
  padding: 0.375rem 0.75rem;
}

容器與內容分離旨在將兩個不同的父子元素給分離出來,藉此達到父子元素不相互依賴的目的。且父子元素只存在名稱上的關係,實際上兩者都可以單獨存在並可以在不同的區域使用。這裡的 .card 就屬於容器,.btn 就屬於內容,如下所示:

  • 容器(container):.container、.col-4、.header;
  • 內容(content):.btn、.input、.dropdown。

需要注意,並非所有的元素都必須遵守容器與內容分離的原則,來看下面的例子:

.col-4 {
  flex: 0 0 100% * (4/12);
  position: relative;
  padding-left: 15px;
  padding-right: 15px;
}

.card {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  word-wrap: break-word;

  &-body {
    margin: 10px auto;
  }
}

一個物件可能同時是容器與內容。對於 .col-4 物件來說,.card 就屬於內容,而對於 .card-body 物件來說,.card就屬於容器。那你可能會想,為什麼不把 .card-body 做分離呢?不是說容器必須與內容作分離嗎?這裡的 .card-body 如果獨立存在本身是沒有任何意義的,需與.card 搭配才會有意義,在這種情況下,.card-body 屬於 .card 的繼承,就無須將其分離出來,與前面的.btn 不同,.btn 獨立存在是可以重複使用在其他元素上的。

通過上面的例子,相信大家已經理解了 OOCSS 的基本思想。其實,Bootstrap 就是根據 OOCSS 規範實現的,來看例子:

<nav class="navbar navbar-light bg-light">
  <a class="navbar-brand">Navbar</a>
  <form class="form-inline">
    <input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search" />
    <button class="btn btn-outline-success my-2 my-sm-0" type="submit">Search</button>
  </form>
</nav>

可以看到,這段程式碼中包含了 .navbar、.navbar-light等 class,這些就屬於 OOCSS 中的結構與樣式分離,而.form-inline、.btn 等 class 就屬於容器與內容分離。如果對 OOCSS 方法論感興趣,可以閱讀一下 Bootstrap 的原始碼,其處理的細膩程度可以說是將 OOCSS 發揮的淋漓盡致。

3. SMACSS

SMACSS 全稱為 Scalable and Moduler Architecture for CSS,意為可擴充套件的模組化 CSS 結構,由 Jonathan Snook 提出。SMACSS 不僅包含了結構與樣式分離的概念,還具有極具特色的結構化命名的概念。所謂的結構化命名,就是將元素做結構分類並限制其命名,以此達到易擴充套件和模組化的目的。

SMACSS 相對於 OOCSS 更偏向於整體結構的分類及模組化 CSS,其中結構的分類包括:

  • Base(基礎):不需要特別的提供字首,且不會使用到class、id​ 選擇器,目的在於設定元素基本樣式。例如:html、*:before、img;
  • Layout(佈局):使用l-​ 或layout-​ 為次要佈局樣式提供字首,目的在於將佈局樣式與其他樣式做區分。例如:.l-header、.l-sidebar、.l-grid;
  • Module(模組):使用塊本身命名為子元素樣式提供字首,目的在於快速瞭解其相關性。例如:.card、.card-header、.card-body;
  • State(狀態):使用is-​ 為狀態樣式提供字首,通過語意化方式瞭解當前狀態。例如:.is-active、.is-hidden、.is-collapsed;
  • Theme(狀態):不需要特別的提供字首,使用物件本身的名稱覆蓋其原先的主題樣式。例如:.l-header-dark、.card-dark。

(1)Base 規則

Base 主要面向某些物件的基本及預設樣式,也就是全域性的初始化(重置)樣式。在編寫這些樣式時應該遵循以下規則:

可以使用元素選擇器、後代選擇器、子選擇器以及任何偽類將基本樣式應用於元素;

不應該使用class、id 選擇器來設定元素預設的樣式;

不應該使用!important 來設定元素預設的樣式(其權重過高,無法被覆蓋)。

參考如下程式碼:

html, form {
  margin: 0;
  padding: 0;
}

*, *:before, *:after {
  box-sizing: border-box;
}

img {
  max-width: 100%;
  height: auto;
}

(2)Layout 規則

SMACSS 中的 Layout 根據重用性將頁面劃分成主要佈局樣式和次要佈局樣式,主要佈局樣式是指不發生重用的元素,而次要佈局樣式就是指會發生重用的元素,在編寫樣式時應該遵守以下規則:

  • 主要佈局樣式通常使用id 選擇器進行設定;
  • 次要佈局樣式通常使用class 選擇器進行設定;
  • 次要佈局樣式可提供l-​ 或layout- 字首用以將佈局樣式與基本樣式做區分;
  • 參考 OOCSS 中的容器與內容分離的概念。

參考如下程式碼:

#header, #article, #footer {
  width: 960px;
  margin: auto;
}

#article {
  border: solid #CCC;
  border-width: 1px 0 0;
}

和 SMACSS 規則不同的是,在 Layout 規則中的主要佈局樣式是可以使用 id 選擇器來定義的。如果想要在特定情況下更改其佈局樣式,可以與次要佈局樣式搭配使用:

#article {
  float: left;
}

#sidebar {
  float: right;
}

.l-flipped #article {
  float: right;
}

.l-flipped #sidebar {
  float: left;
}

根據 CSS 層疊的特性,可以讓元素應用到更高層的佈局樣式,以覆蓋其預設的樣式。這裡需要注意,所謂的主要佈局樣式和次要佈局樣式都只是名稱上的定義,不要將自己的思維侷限在只能使用主要佈局樣式,也就是全部使用 id選擇器來編寫佈局樣式。大部分情況下,次要佈局樣式比主要佈局樣式使用的更多。參考下面的例子:

<div id="featured">
  <h2>Featured</h2>
  <ul>
    <li><a href="…">…</a></li>
    <li><a href="…">…</a></li>
    …
  </ul>
</div>

如果不考慮 SMACSS 中的次要佈局樣式寫法,我們可能會為 div 新增名為featured的id,然後通過 id 選擇器來設定樣式:

.l-grid {
  margin: 0;
  padding: 0;
  list-style-type: none;
}

.l-grid > li {
  display: inline-block;
  margin: 0 0 10px 10px;
}

這樣就相當於把元素完全繫結死了,這裡的 #featured 只能用在div 標籤上。這不就是 OOCSS 要解決的問題嗎?可以根據 SMACSS 中的次要佈局樣式規則來解決這個問題:

.l-grid {
  margin: 0;
  padding: 0;
  list-style-type: none;
}

.l-grid > li {
  display: inline-block;
  margin: 0 0 10px 10px;
}

其實 SMACSS 中的次要佈局樣式就像是 OOCSS 中的容器與內容分離原則,目的都是將依賴性降到最低。其實對於次要佈局樣式,就相當於在 OOCSS 的基礎上,加上其命名限制中的 -l 字首就可以了。

(3)Module 規則

Module 主要面向應用中的可重用元素的樣式,與 Layout 不同的地方在於其元素更為準確。基於 Module 的元素都應該以獨立元素的方式存在。在編寫是需要遵循以下規則:

不應該使用元素選擇器、id 選擇器設定元素樣式;

  • 僅使用class 選擇器設定元素樣式;
  • 使用元素本身命名為子元素樣式提供字首;
  • 參考 OOCSS 中的結構與樣式分離概念。

參考如下程式碼:

<div class="card">
  <div>Card Header</div>
  <div>Card Footer</div>
</div>

這時我們可能會這樣編寫樣式:

.card > div {
  padding-left: 20px;
}

這樣寫的問題在於,.card 中的 div 被繫結死了,如果想要針對裡面不同的 div 編寫樣式,就需要做出調整。SMACSS 中的 Module 建議都使用 class 選擇器來編寫樣式:

.card-header {
  padding-left: 20px;
}

.card-footer {
  padding-left: 20px;
}

這樣就解決了 div 被繫結死的問題,同時代碼的可讀性也增加了。SMACSS 的作者建議不要使用div、span這種元素選擇器來定義樣式,而是使用 class 選擇器來強調語意化及可重用性。繼續來看下面的例子:

.pod {
  width: 100%;
}
.pod input[type='text'] {
  width: 50%;
}
#sidebar .pod input[type='text'] {
  width: 100%;
}

前面提到,基於 Module 的元素應該能夠在應用的任意部分使用。這時編寫的的樣式就會像上面的程式碼這樣,程式碼越來越複雜,可能一不小心就忽略了CSS的優先順序,導致樣式被錯誤的覆蓋。SMACSS 建議使用以下方式來寫:

.pod {
  width: 100%;
}

.pod input[type='text'] {
  width: 50%;
}

.pod-constrained input[type='text'] {
  width: 100%;
}

然後為指定物件新增元素與子元素 class 名稱:

<div class="pod pod-constrained">...</div>

其實它的概念就像 OOCSS 中的結構與樣式分離,只不過這裡稱之為子類化(Subclassing),通過將樣式抽離出來,以後再不同元素中使用模組時,只需要新增模組名稱與子類化模組名稱即可。

(4)State 規則

State 主要面向 Layout 或 Module 在應用上的特效及動作,其概念類似於 BEM 中的Modifier,為了保證樣式可以作用於物件,允許使用 @important。在編寫時可以參照以下規則:

  • State 可以巢狀在 Layout 或 Module 中;
  • 可以使用 JavaScript 改變樣式;
  • 提供 is- 字首用以區分此樣式為狀態樣式;
  • 可以合理的使用!important 來覆蓋樣式;

參考如下程式碼:

<ul class="nav">
  <li class="nav-item">
    <a class="nav-link is-active" href="#">Link</a>
  </li>
  <li class="nav-item">
    <a class="nav-link" href="#">Link</a>
  </li>
  <li class="nav-item">
    <a class="nav-link is-disabled" href="#">Link</a>
  </li>
</ul>

與上面介紹的子模組樣式不同的地方在於,狀態樣式不需要繼承於任何物件,而只是單純的將樣式應用於物件,可以參考 Bootstrap 中的 .active 或 .disabled 樣式,作用就類似於上面的 is-active 與 is-disabled,為了保證狀態樣式可以作用於指定物件,在 State 中允許使用 @important。

(5)Theme 規則

Theme 主要面向應用中為主視覺定義的 Layout 或 Module 樣式,例如主題切換。在編寫時需要遵循以下規則:

  • 直接使用 Layout 或 Module 定義的class 覆蓋其樣式。

參考如下程式碼:

// index.css
.mod {
  border: 1px solid;
}

// themeA.css
.mod {
  border-color: blue;
}

這裡需要注意,不需要使用獨立的 class 去新增主題樣式,在 Theme 的規則中建議使用與原來 Layout 或 Module 中相同的樣式名稱,而 themeA.css 樣式會在 index.css 之後才載入,這樣就可以達到覆蓋樣式的目的,如果應用中有很多主題樣式,也只需要新增像 themeB.css 這樣的樣式檔案即可:

.mod {
  border-color: red;
}

4. ITCSS

ITCSS 全稱為 Inverted Triangle CSS,意為倒三角CSS,由 Harry Robers 開發。ITCSS 是一種可擴充套件和可管理的架構,獨立於前處理器存在。它出現的主要目的是幫助組織專案的 CSS 檔案,從而解決由級聯和選擇器的特殊性引起的問題。

ITCSS 的目標是通過分層組織 CSS 檔案,實現了自下而上的特異性。它基於分層的概念把專案中的樣式分為七層:

上層定義的程式碼比下層定義的程式碼影響更大。因此,上層會影響下層,下層不會影響上層。下層將繼承上級的樣式,越往下越具體。

ITCSS 通過三個關鍵指標對 CSS 專案進行排序:

  • 通用到顯式(explicitness):在 ITCSS 的分層中,每一層的權重是越來越大,作用的範圍越來越小。從通用的規則到非常明確的規則;
  • 低特異性到高特異性(specificity):開始的時候選擇器具有最低的特異性(優先順序),隨著層數的增加,特異性也在不斷變大。因此,要儘量避免在低特異性選擇器之前編寫高特異性選擇器;
  • 深遠到本地化(reach):上層定義的樣式會影響很多HTML的表現,隨著層數的增加,影響範圍逐漸減小。

(1)SETTINGS

第一層 SETTINGS 表示設定,這一層包含專案的所有全域性設定。通常會定義一些全域性變數,例如顏色、字型大小等,這一層不會生成實際的 CSS。

$main-color: #6834cb;
$main-font-size: 24px;

(2)TOOLS

第二層 TOOLS 表示工具,如果使用了前處理器,可以在這一層定義 function 和 mixins。Tools 層位於 Settings 層之後,因為 mixin 可能需要全域性設定中的一些變數來作為預設引數。同樣,這一層也不會生成實際的 CSS。

@function sum($numbers...) {
  $sum: 0;
  @each $number in $numbers {
    $sum: $sum + $number;
  }
  @return $sum;
}

@mixin sample-mixin () {
  ...
}

(3)GENERIC

第三層 GENERIC 表示通用,可以在這一層來定義重置或者標準化瀏覽器的基本樣式,這一層很少會被修改。這也是第一個實際會生成 CSS 的層。

* {
  padding: 0;
  margin: 0;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

(4)ELEMENTS

第四層 ELEMENTS 表示元素,通常用來定義影響 HTML 單個標籤的樣式,例如 h1、p 標籤的預設樣式:

h1 {
  color: $main-color;
  font-size: $main-font-size;
}

(5)OBJECTS

第五層 OBJECTS 表示物件,可以在這一層定義整個專案中可重用的頁面結構類。與上一層相比,這一層對 DOM 的影響更小,具有更高的特異性(優先順序),並且更加明確,因為現在將 DOM 的部分作為目標來設定了樣式。

.grid-container {
  display: grid;
  grid-template-columns: auto auto auto auto;
}

(6)COMPONENTS

第六層 COMPONENTS  表示 UI 元件,與物件不用,元件是頁面的特定部分。比如搜尋框的樣式,為元件定義的樣式只會影響到對應的元件。這一層比上一層更加明確,因為現在為 DOM 設計了明確的樣式。

.c-btn {
  display: flex;
  justify-content: center;
  align-items: center;
  ...

  &--primary {
    background-color: #ff5959;
    color: #fff;
  }

  &--large {
    font-size: 16px;
    padding: 16px 14px;
    ...
  }
}

(7)TRUMPS

這一層也稱為 Utilities,包含所有那些覆蓋之前層中定義的任何其他規則的規則。它是唯一允許使用 !important 的層。

.d-none {
  display: none!important;
}

(8)專案結構

那這七層結構的 CSS 檔案該如何組織呢?主要有兩種方式:

  • 每一層一個資料夾:

  • 檔名使用層的名字作為字首:

在使用樣式時,就需要按照層的順序來引用這些 CSS,就像這樣:

@import "settings.global.scss";
@import "settings.colors.scss";

@import "tools.functions.scss";
@import "tools.mixins.scss";

@import "generic.box-sizing.scss";
@import "generic.normalize.scss";

@import "elements.headings.scss";
@import "elements.links.scss";

@import "objects.wrappers.scss";
@import "objects.grid.scss";

@import "components.site-nav.scss";
@import "components.buttons.scss";
@import "components.carousel.scss";

@import "trumps.clearfix.scss";
@import "trumps.utilities.scss";
@import "trumps.ie8.scss";

5. ACSS

ACSS 的全稱為 Atomic CSS,意為原子CSS。它專注於建立很多小型的 CSS 樣式類,以便在 HTML 上使用。這種方法旨在提供高度精細和可重用的樣式,而不是為每個元件提供規則。這可以減少特異性(優先順序)衝突並以可預測的方式使樣式更具可變性。這種方法有助於減少程式碼冗餘和覆蓋 CSS 樣式的混淆。

參考以下程式碼:

.mb-sm { margin-bottom: 16px; }
.mb-lg { margin-bottom: 32px; }
.color-blue { color: #1e90ff; }

在HTML中這樣來使用:

<div class="mb-lg">
 <p class="mb-lg color-blue">Blue text</p>
 <img class="mb-sm" />
</div>

ACSS 有一些程式設計方法,可以根據使用者新增到 HTML 的類或屬性自動生成 CSS。Atomizer 就是這樣的一個工具,它允許將 HTML 進行如下定義:

<div class="Mb(32px)">
 <p class="Mb(32px) C(#1e90ff)">Blue text</p>
 <img class="Mb(16px)" />
</div>

這樣在構建時就會自動生成以下CSS:

.Mb\(16px\)   
{ margin-bottom: 16px; }
.Mb\(32px\)   
{ margin-bottom: 32px; }
.C\(#1e90ff\) { color: #1e90ff; }

注意,單獨使用 ACSS 會導致類的數量多到難以管理,並且 HTML 結構會非常臃腫。因此,通常只會使用 ACSS 原則來建立定義一致、可重用的宣告塊的輔助類。