Skip to content

SMACSS:把樣式分成五種類別

SMACSS:把樣式分成五種類別

SMACSS(Scalable and Modular Architecture for CSS)不規定名字怎麼取,也不規定樣式怎麼拆,它回答的是另一個問題:這段新樣式該寫到哪裡去。做法是把整份樣式分成五種類別。

五種分類

分類收什麼命名
Base 基礎元素本身的預設樣式、重置:root變數元素選取器,不加類別
Layout 版面把畫面空間切出來的骨架:頁首、側欄、主內容區l- 前綴
Module 模組放進版面裡、可重複使用的元件:卡片、按鈕、分頁元件自己的名字
State 狀態執行期才會切換的狀態:展開、載入中、有錯誤is- 前綴
Theme 主題換膚才會改變的部分:配色、字體theme- 前綴或變數
css
/* Base */
body { margin: 0; font: 16px/1.6 system-ui, sans-serif; }

/* Layout */
.l-container { max-width: 1200px; margin-inline: auto; }
.l-sidebar { flex: 0 0 240px; }

/* Module */
.card { background: #fff; border-radius: 8px; }
.card__title { font-size: 1.25rem; }

/* State */
.is-hidden { display: none; }
.card.is-loading { opacity: .5; pointer-events: none; }

/* Theme */
.theme-dark { --surface: #1f2937; --text: #f9fafb; }

Base:只寫元素,不寫類別

Base 是唯一大量使用 元素選取器 的分類,因為它定義的是「這個網站的預設長相」:

css
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; color: #1f2937; }
a { color: #2965f1; }

不要在這裡寫任何類別。一旦 Base 出現 .btn,它就該搬到 Module 去了。

Layout 與 Module 的分界

這是最容易混淆的一組。判斷標準只有一句:它負責切空間,還是負責填內容

css
/* Layout:把空間切出來 */
.l-page { display: grid; grid-template-columns: 240px 1fr; gap: 24px; }

/* Module:放進那些空間裡的東西 */
.card { background: #fff; padding: 16px; }

還有兩個實務上的差別:

  • Layout 通常在頁面上只出現一次,Module 則會重複很多次。
  • Layout 可以用 ID(SMACSS 原著允許 #header 這種寫法),Module 絕不用。不過現在多數專案一律用類別,避免 權重 不一致。

State:交給 JavaScript 切換的那一層

State 和 Module 的修飾子很像,差別在 誰決定它什麼時候出現

css
/* Module 的變體:寫死在 HTML 裡 */
.btn--primary { background: #2965f1; }

/* State:執行期才會加上去 */
.is-disabled { opacity: .5; pointer-events: none; }
js
btn.classList.add("is-disabled");

is- 前綴的價值在於一眼分辨:看到它就知道要去 JavaScript 裡找誰加的。這個慣例被廣泛借用,和 BEM 並用非常常見。

小提醒

State 是唯一「可以用兩個類別串起來」的分類,例如 .card.is-loading。這是為了讓狀態只在特定模組上生效,權重只多一層,還在可控範圍。

Theme:多數專案用不到

Theme 只在真的要換膚時才需要獨立成一類。現在多半改用 CSS 變數 實作,比整組類別覆蓋乾淨:

css
:root { --surface: #fff; --text: #1f2937; }
[data-theme="dark"] { --surface: #1f2937; --text: #f9fafb; }

.card { background: var(--surface); color: var(--text); }

Sass 搭配變數做主題切換的完整做法見 主題切換

載入順序很重要

五類的順序不是隨便排的,它決定了誰蓋得掉誰:

Base → Layout → Module → State → Theme

權重相同時,後寫的勝出。把 State 放在 Module 之後,.is-hidden 才蓋得掉模組自己的 display,不必再靠 !important。這和 Sass 7-1 檔案架構 強調載入順序是同一個道理。

小專案怎麼簡化

不必為了湊滿五類而硬分:

情況建議
沒有換膚需求拿掉 Theme,配色直接放 Base 的變數。
版面單純Layout 併進 Base,只留三類。
元件很少五類合成一個檔案,用註解分段就好。

注意

分類的價值在於 回答「這段樣式寫哪裡」。若團隊對某一類的邊界始終沒有共識,那一類就沒發揮作用,合併掉反而更好。

常見問題

Layout 和 Module 怎麼分?

看它有沒有內容。Layout 是版面的骨架,負責把空間切出來,例如頁首、側欄、主內容區;Module 是放進那些空間裡、可以重複使用的東西,例如卡片與按鈕。

五類的載入順序可以調換嗎?

不建議。順序決定了誰蓋得掉誰,Base 在最前、State 與 Theme 在最後,才能用書寫順序解決覆蓋問題而不必提高 權重

State 一定要用 is- 前綴嗎?

不是硬性規定,但很值得。看到 is- 就知道這個類別是由 JavaScript 動態切換的,搜尋與除錯時能立刻把它和固定樣式分開。

小專案要湊滿五類嗎?

不用。沒有換膚需求就不必有 Theme,版面單純的話 Layout 也可以併進 Base。留下 Base、Module、State 三類,已經足以回答新樣式該寫在哪裡。

延伸閱讀