Skip to content

OOCSS:結構與外觀分離的兩個原則

OOCSS:結構與外觀分離的兩個原則

OOCSS(Object Oriented CSS,物件導向 CSS)把畫面上重複出現的東西看成可以組合的物件,用兩個原則減少重複的樣式。它比 BEM 更早出現,關心的不是名字怎麼取,而是 樣式該怎麼拆

原則一:結構與外觀分離

同一顆按鈕,把「骨架」和「長相」分開寫:

css
/* 結構:所有按鈕共用 */
.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; }
html
<button class="btn btn-primary">送出</button>
<button class="btn btn-danger">刪除</button>

沒有分離的話,三顆按鈕會各自把 paddingborder-radius 再抄一次;要調圓角就得改三個地方,而且總有一個會漏掉。

小提醒

判斷標準很單純:改版時會一起變的放一起。圓角與內距是整組按鈕的共同語彙,配色是個別用途的差異。

原則二:容器與內容分離

元件的樣式不該依賴它被放在哪裡。

css
/* ❌ 樣式綁著容器,元件搬到頁尾就失效 */
.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; }

這條原則同時解決了兩個麻煩:權重 不會因為後代選取器而疊高,元件也真的能被重複使用,這正是 模組化 這三個字的意思。

位置交給容器決定

延伸出一條實務上很常被違反的規則:外距與寬度不寫在元件身上

css
/* ❌ btn 自己決定了跟別人的距離,換個版面就不對 */
.btn { margin-right: 16px; }

/* ✅ 距離由放它的地方決定 */
.form-actions { display: flex; gap: 16px; }

元件負責自己長什麼樣,容器負責它擺在哪。用 彈性盒子gap 處理間距,比讓每個元件各自帶 margin 乾淨得多。

一個完整的例子

把卡片拆成可組合的幾塊:

css
/* 結構物件 */
.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; }
html
<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 拆到多細才合適?

以複製第三次為界。同一組樣式抄到第三個地方時再抽成共用基底;太早抽象,只會抽出一個之後誰都不敢改的類別。

延伸閱讀