CSS 設計模式|OOCSS|SMACSS|BEM|ITCSS|Atomic CSS

CSS 只要有學過,人人都會寫,但是要怎麼設計 CSS Design Patterns 又是另一個學問,為什麼要學習 CSS Design Patterns 呢?我們常見的 css 有幾種問題:

  1. CSS 重複性高
  2. 很常遇到權重問題
  3. 命名相衝問題

以下我來介紹五種 CSS 設計方法。

OOCSS

Nicole Sullivan 在 2009 年提出,那時候已經有漸漸複雜的網頁開始出現,Nicole Sullivan 整理出適用 CSS 的簡易模式,也就是區分兩種分類方式,像是 Structure 和 Style,Container 和 Content。

優點:
– 減少冗長的 css
– 減少 css 的檔案大小

缺點:
– 由於太基礎,在複雜的架構反而顯得不夠明確

Rules:
1. Separate Structure & Style
2. Separate Container & Content

什麼是 結構(Structure) 與 樣式(Style) ?

Structure 就是我們 CSS 屬性的結構樣式,像是 width、height、margin、padding,那 Style 就是裝飾性的可變樣式,像是 font、color、shadow、gradient。

不好的寫法如下:(過多重複的 CSS 在選擇器裡)

.button-bg-blue {
  border: solid 1px #00afb8;
  background-color: #00afb8;
  color: #fff;
  font-size: 14px;
  line-height: 1.45;
  width: 120px;
  height: 32px;
  border-radius: 4px;
  font-weight: bold;
  text-align: center;
}

.button-bg-white {
  border: solid 1px #00afb8;
  background-color: #fff;
  color: #00afb8;
  font-size: 14px;
  line-height: 1.45;
  width: 120px;
  height: 32px;
  border-radius: 4px;
  font-weight: bold;
  text-align: center;
}
<button class="button-bg-blue" type="submit">確定</button>
<button class="button-bg-white" type="reset">取消</button>

以 OOCSS 的方法就是切分可重複利用的部分:

.btn {
  font-size: 14px;
  line-height: 1.45;
  width: 120px;
  height: 32px;
  border-radius: 4px;
  font-weight: bold;
  text-align: center;
}

.btn:hover {
 opacity: 0.7; 
}

.btn-bg-blue {
  border: solid 1px #00afb8;
  background-color: #00afb8;
  color: #fff;
}

.btn-bg-white {
  border: solid 1px #00afb8;
  background-color: #fff;
  color: #00afb8;
}
<button class="btn btn-bg-blue" type="submit">確定</button>
<button class="btn btn-bg-white" type="reset">取消</button>

這樣大大減少了重複的結構來達到最佳的復用性。

什麼是 容器(Container) 與 內容(Content)?

其實就是區分 Separate 元素與 CSS 組合,OOCSS 想要做到的就是:

  1. 避免使用過多巢狀選擇器(.Container > .Content)
  2. 樣板元件化與重複利用

不好的案例如下:(依賴 .list 的存在)

.list .top {
  display: block;
  width: 100%;
  height: 150px;
  border-radius: 4px;
}

.list .content {
  padding: 10px 0; 
}

.list .content .avatar {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  overflow: hidden;
}
<div class="list">
  <div class="top">
    <img src="" alt="" />
  </div>
  <div class="content">
    <div class="avatar">
      <img src="" alt="" />
    </div>
  </div>
</div>

如果發現其他樣板也需要重複使用 .avatar,就會發現無法重複使用移到另一個 container,因為 .avatar 已經被綁定在 .list 的子選擇內。

那 Concept 要做到切分乾淨,就是簡易的將階級同等與獨立出來就可以達到重複利用,如下:

.list-top {
  display: block;
  width: 100%;
  height: 150px;
  border-radius: 4px;
}

.list-content {
  padding: 10px 0; 
}

.avatar-content {
  padding: 10px 0; 
}

.avatar-context {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  overflow: hidden;
}
<div class="list-top">
  <div class="list-content">
    <img src="" alt="" />
  </div>
  <div class="avatar">
    <div class="avatar-content">
      <img src="" alt="" />
    </div>
    <div class="avatar-context">
      <p>我是內容1</p>
    </div>
  </div>
</div>

<div class="main-top">
  <div class="avatar">
    <div class="avatar-content">
      <img src="" alt="" />
    </div>
    <div class="avatar-context">
      <p>我是內容2</p>
    </div>
  </div>
</div>

Content 變成 Container,更加元件化。

不管任何的 CSS 設計方法其實這個觀念是很重要的,比如 BEM 也是這樣,如果你無法想像這帶來的好處,那你可以思考一下原本像依賴寫法會帶來什麼好處,其實這都沒有對錯,只要知道自己為什麼這麼做就好了!

總結

學程式有一句老話,就是『多用合成少用繼承』,CSS 也是如此,多合成 Container 與 Content,避免 Content 繼承自 Container,學 OOCSS 就是為了 DRY(Don’t Repeat Yourself)

SMACSS

Jonathan Snook 在 2011 年提出的,主要是將 CSS 分為五大種類:

  1. Base
  2. Layout
  3. Module
  4. State
  5. Theme

Base

也就是基礎的 Normailze 與 Reaet 樣式,在規則上只可以採用:

  • Element(Type) Selectors:h1, h2 ……
  • Descendent Selectors: h1 em, div p span …..
  • Child Selectors:ol > li ……
  • Pseudo-class:(偽元素) :before, :after, :hover …..
  • 也就是 reset.css, normailze.css 等等
body, form {
  margin: 0;
  padding: 0;
}

a {
  color: #039; 
}

a:hover {
  color: #03f; 
}

Layout

  • 使用 ID Selector,或者 Class Selector
  • Class 命名方式,prefix: .l-xxx
  • 每個 Layout 是唯一的
  • 可以利用 Class 壓權重做切換調整
#article {
  width: 70%;
  float: left;
}

#sidebar {
  width: 30%; 
  float: right;
}

.l-fixed #article {
  width: 700px; 
}

.l-fixed #sidebar {
  width: 300px; 
}

Modules

  • 可重複利用的模組
  • 避免使用 ID Selector 或者 Element Selector
  • 只使用 Class Selector
  • 可以使用後代選擇器,但限於它是可預期的
.module {
  width: 100%; 
}

.module > h2 {
  padding: 10px; 
}

.module span {
  padding: 5px; 
}

State

  • 狀態樣式,複寫屬性
  • 命名方式,prefix: .is-xxx
  • 只有 stete 允許下 !important,但也僅在必要時
  • 與互動有關通常會搭配 js 使用
<div id="top" class="is-collapsed">
  <form>
    <div class="msg">hello</div>
    <label class="is-hidden">
      no...
    </label>
    <input type="text" id="username" />
  </form>
</div>

Theme

  • 與 State 很相似,複寫屬性
  • 可改變 module/layout 的樣式,如:color, border
  • 廣泛的命名方式,prefix:.theme-xxx
.theme-border {
  border-color: red; 
}

.theme-background {
  background: linear-gradient( ... ); 
}

總結

OOCSS 與 SMASS 方法是可以互補的,學了 OOCSS 不夠,再加入 SMACSS,就可以幫助你開發出一套較大的 CSS 框架,SMACSS 在分類與規則在使用上仍有些分工得不夠詳細,比如 Layout 有大也有小,但我們仍可以提取好的部分,像是 State 在語義與實用上非常明確。

BEM

是由 Yandex 所提出的一套方法論,大約是在 2016 2017 年左右盛行起來,核心概念如下:

  • Block
  • Element
  • Modifier

優點:
– 簡單好學
– 減少命名相衝的發生率

缺點:
– 沒想好命名也許會變得很長

缺點範例如下:

<span class="abc-user-overview__status abc-user-overview__status_active">太長啦,啊不然勒你想怎樣</span>

BEM 的概念主要是用連字符號來區分三種類型,像是 Block,它是用單橫線來串連,Element 是用雙底線來表示,Modifier 是用雙橫線來表示。

Block

Block 在 BEM 定義中是區塊元素,可以想像成 component,所有 Block 的 CSS 權重都是平等階級的。

Block 在 HTML 的使用可以當作是 content 去做 nested 使用,也可以被當成 Container 使用。

<div class="block">
  <div class="block__title">標題</div>
  <div class="user-block">
    <div class="user-block__name">大中天</div>
  </div>
</div>
.block {
  padding: 20px; 
}

.user-block {
  margin-top: 20px; 
}

看到這裡,這不就符合 OOCSS 第二個概念嘛~

Element __

Element 是用雙底線表示,語意命名上需要跟 Block 做綁定,在 CSS 權重上跟 Block 是平等的且不能依賴於 Block。

<div class="block">
  <div class="block__title"></div>
  <div class="block__info"></div>
</div>
.block {
  margin: 10px;
}

.block__title {
  font-size: 20px; 
}

.block__info {
  padding: 10px 0; 
}

/* 以下 BEM 不推薦寫法 */

.block .block__title {
  font-size: 20px; 
}

.block .block__info {
  padding: 10px 0; 
}

Modifier —

改變 appearance、behavior 和 state,定義上只採用 Class name,遠永不會單獨在 html element 出現,命名皆需綁定自 Block 與 Element,可以直接壓權重複寫 Element,用處是改變 Block 或 element 的樣式狀態。

<div class="block">
  <div class="block__title block__title--large"></div>
  <div class="block__info"></div>
</div>
.block {
  margin: 10px;
}

.block--dark {
  ... 
}

.block__title {
  font-size: 20px; 
}

.block__info {
  padding: 10px 0; 
}

.block__info--large {
  color: red; 
}

總結

  • 簡單好用
  • 大大減少命名相衝的機率
  • Block 就是可重復利用的元件/模組/Container/whatever
  • Element 也是 Block 的 Content
  • Modifier 可改變 Block 或 Element 的樣式/行為/狀態

ITCSS

是由 Harry Roberts 提出的概念,知道的人比較少,它是基於 SASS 方法架構,除了分類分得更詳細,也提供了圖像的解說幫助理解。

CSS 設計模式|OOCSS|SMACSS|BEM|ITCSS|Atomic CSS
ITCSS

概念圖是倒三角形,從上到下分別為七大類,當然權重也是越上面越高,使用目的也會越來越明確,七大類如下:

  • Settings:Global variables, config
  • Tools:Mixins and functions
  • Generic:Normalize.css, resets, box-sizing
  • Base:Unclassed Html elements [type selectors]
  • Object:Cosmetic-free design patterns (oocss)
  • Components: Designed components, chunks of UI
  • Trumps:Helpers and overrides

總結

  • 基於 Sass
  • 提供 css 層級管理分類定義
  • 快速了解各 css 屬性該在的位置
  • 幫助建立一套可維護的 css 框架
  • 與 Bootstrap 的 scss 分類方式其實大同小異

Atomic CSS

也可以叫 Functional CSS 或 Utility-First CSS,概念都是一樣的,都是讓 class 用成單一原子化,Atomic CSS 把它定義為 css for component – based frameworks,也就是適合 js 元件化開發,如 react、vue 等 css 方法,寫起來像是寫 inline style。

優點:
– 原子化,利於重複使用
– 專注在 html 合成 class selector

缺點:
– Html class 會很長

<div class="user-card"></div>
.user-card {
  padding: 20px;
  margin: 20px;
  color: #eee;
  background: #333;
  border: 1px solid #555;
}

Atomic CSS 寫法為:

<div class="M(20px) P(20px) C(#eee) Bgc(#333) Bd(1px) Bdc(#555)">...</div>

Bootstrap utilities / Tailwind css:

<div class="m-5 p-5 text-gray-light bg-gray-darker border border-gray-light">...</div>

以上 CSS 的基本重點

不管以上你用到哪一種模式或是混用哪些模式重點不外乎如下:

  • 維持低權重的 Class Selectors 方式撰寫
  • 懂得區分結構與樣式,懂得區分容器與內容
  • 多使用合成的方式 exp:.btn .btn-primary .btn–rounded
  • 懂得運用 Grid 概念,使用寬來控制區塊,減少定義 height
  • 盡量避免使用 !important,除非權重已經無藥可救(或知道自己為什麼用)

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *