OOCSS:結構與外觀分離的兩個原則
OOCSS(Object Oriented CSS,物件導向 CSS)把畫面上重複出現的東西看成可以組合的物件,用兩個原則減少重複的樣式。它比 BEM 更早出現,關心的不是名字怎麼取,而是 樣式該怎麼拆。
原則一:結構與外觀分離
同一顆按鈕,把「骨架」和「長相」分開寫:
/* 結構:所有按鈕共用 */
.btn {
display: inline-block;
padding: 8px 16px;
border: 0;
border-radius: 4px;
cursor: pointer;
}
/* 外觀:各自獨立 */
.btn-primary { background: #2965f1; color: #fff; }
.btn-danger { background: #dc2626; color: #fff; }
.btn-ghost { background: transparent; color: #2965f1; box-shadow: inset 0 0 0 1px currentColor; }<button class="btn btn-primary">送出</button>
<button class="btn btn-danger">刪除</button>沒有分離的話,三顆按鈕會各自把 padding、border-radius 再抄一次;要調圓角就得改三個地方,而且總有一個會漏掉。
小提醒
判斷標準很單純:改版時會一起變的放一起。圓角與內距是整組按鈕的共同語彙,配色是個別用途的差異。
原則二:容器與內容分離
元件的樣式不該依賴它被放在哪裡。
/* ❌ 樣式綁著容器,元件搬到頁尾就失效 */
.sidebar ul { list-style: none; padding: 0; }
.sidebar ul li { padding: 8px 0; border-bottom: 1px solid #e5e7eb; }
/* ✅ 給元件自己的名字,放哪裡都一樣 */
.nav-list { list-style: none; padding: 0; }
.nav-list-item { padding: 8px 0; border-bottom: 1px solid #e5e7eb; }這條原則同時解決了兩個麻煩:權重 不會因為後代選取器而疊高,元件也真的能被重複使用,這正是 模組化 這三個字的意思。
位置交給容器決定
延伸出一條實務上很常被違反的規則:外距與寬度不寫在元件身上。
/* ❌ btn 自己決定了跟別人的距離,換個版面就不對 */
.btn { margin-right: 16px; }
/* ✅ 距離由放它的地方決定 */
.form-actions { display: flex; gap: 16px; }元件負責自己長什麼樣,容器負責它擺在哪。用 彈性盒子 的 gap 處理間距,比讓每個元件各自帶 margin 乾淨得多。
一個完整的例子
把卡片拆成可組合的幾塊:
/* 結構物件 */
.media { display: flex; gap: 12px; }
.media-figure { flex: 0 0 auto; }
.media-body { flex: 1; min-width: 0; }
/* 外觀物件 */
.surface { background: #fff; border-radius: 8px; box-shadow: 0 1px 3px rgb(0 0 0 / .1); }
.surface-flat { box-shadow: none; border: 1px solid #e5e7eb; }<article class="media surface">
<div class="media-figure"><img src="avatar.jpg" alt=""></div>
<div class="media-body">
<h3>標題</h3>
<p>內文</p>
</div>
</article>media 只管排列、surface 只管外觀,任何需要「圖文並排」的地方都能重用 media,不必再寫一次 flex。
它的代價
| 代價 | 說明 |
|---|---|
| HTML 類別變多 | class="media surface surface-flat" 是常態,讀 HTML 時得同時想到好幾個類別。 |
| 樣式來源變分散 | 一個元件的完整長相由多個類別疊出來,除錯時要一起看。 |
| 容易抽象過頭 | 為了重用而硬湊出來的物件,往往只被用過一次。 |
注意
別太早抽共用類別。實務上的分寸是 複製到第三次再抽:前兩次的重複還看不出真正的共同點,太早抽出來的抽象通常是錯的,而且之後誰都不敢改。
和工具類別的界線
把每個屬性都做成一個類別(.mt-4、.text-center)是另一套思路,通常叫工具類別或 Atomic CSS。它和 OOCSS 的差別在粒度:
| OOCSS | 工具類別 | |
|---|---|---|
| 一個類別的範圍 | 一組樣式(一顆按鈕的骨架) | 一個屬性(一個外距值) |
| 改版時 | 改 CSS | 改 HTML |
| 適合 | 有明確元件語彙的專案 | 版型變化多、元件難以定型的專案 |
兩者可以並用:元件用 OOCSS 拆,零星的微調用工具類別補。Sass 批次產生工具類別的做法見 工具類別。
常見問題
OOCSS 和 BEM 衝突嗎?
不衝突,兩者管的層次不同。OOCSS 決定樣式怎麼拆成基底與變化,BEM 決定拆出來的東西叫什麼名字,實務上常一起用。
什麼是容器與內容分離?
指元件的樣式不該依賴它被放在哪裡。用後代選取器寫成「側欄裡的清單長這樣」,元件搬到別處就失效;改成給元件自己一個類別,放哪裡都成立。
外距和寬度該寫在元件身上嗎?
不該。外距與寬度屬於元件在版面中的位置,由外層容器決定。寫在元件自己身上,它在別的版面裡就會格格不入。
OOCSS 拆到多細才合適?
以複製第三次為界。同一組樣式抄到第三個地方時再抽成共用基底;太早抽象,只會抽出一個之後誰都不敢改的類別。