SMACSS:把樣式分成五種類別
SMACSS(Scalable and Modular Architecture for CSS)不規定名字怎麼取,也不規定樣式怎麼拆,它回答的是另一個問題:這段新樣式該寫到哪裡去。做法是把整份樣式分成五種類別。
五種分類
| 分類 | 收什麼 | 命名 |
|---|---|---|
| Base 基礎 | 元素本身的預設樣式、重置、:root 的 變數 | 元素選取器,不加類別 |
| Layout 版面 | 把畫面空間切出來的骨架:頁首、側欄、主內容區 | l- 前綴 |
| Module 模組 | 放進版面裡、可重複使用的元件:卡片、按鈕、分頁 | 元件自己的名字 |
| State 狀態 | 執行期才會切換的狀態:展開、載入中、有錯誤 | is- 前綴 |
| Theme 主題 | 換膚才會改變的部分:配色、字體 | theme- 前綴或變數 |
/* 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 是唯一大量使用 元素選取器 的分類,因為它定義的是「這個網站的預設長相」:
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; color: #1f2937; }
a { color: #2965f1; }不要在這裡寫任何類別。一旦 Base 出現 .btn,它就該搬到 Module 去了。
Layout 與 Module 的分界
這是最容易混淆的一組。判斷標準只有一句:它負責切空間,還是負責填內容。
/* 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 的修飾子很像,差別在 誰決定它什麼時候出現:
/* Module 的變體:寫死在 HTML 裡 */
.btn--primary { background: #2965f1; }
/* State:執行期才會加上去 */
.is-disabled { opacity: .5; pointer-events: none; }btn.classList.add("is-disabled");is- 前綴的價值在於一眼分辨:看到它就知道要去 JavaScript 裡找誰加的。這個慣例被廣泛借用,和 BEM 並用非常常見。
小提醒
State 是唯一「可以用兩個類別串起來」的分類,例如 .card.is-loading。這是為了讓狀態只在特定模組上生效,權重只多一層,還在可控範圍。
Theme:多數專案用不到
Theme 只在真的要換膚時才需要獨立成一類。現在多半改用 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 三類,已經足以回答新樣式該寫在哪裡。