Nic Lin's Blog

喜歡在地上滾的工程師

搞懂 sass 中的 BEM 設計原則

最近常常在專案裡面看到令人很頭痛的 CSS,沒有設計模式,有大量不明白的階層跟命名,完全無法複用的元素XDD,心中是滿滿的 fuck。

於是我就開始對 Sass 產生好奇了,也從 BEM 的開發模式思考了一些命名和規則

BEM由Yandex團隊提出來的一種創新命名Class名稱的設計模式,BEM的意思是區塊(Block)、元素(Element)、修飾符(Modifier)

其中最好玩的就是,我們可以從瀏覽 class 名稱就能知道這些元素彼此之間的關係

讓我們來讀一下一般最常見到的結構吧

<div class="product">
  <ul class="menu">
      <li class="item active"><a href="#"></a></li>
      <li class="item"><a href="#"></a></li>
  </ul>
</div>

然後就會開始思考一些問題了

  • product 與 menu 的關聯? .product .menu
  • active 是否只侷限於 list 的範圍域?
  • menu 是可以獨立拆分出來的 Modular 嗎?還是其實是綁在 product 下面?

在使用 BEM 的命名原則之後

<div class="product">
    <div class="menu">
        <li class="menu__item menu__item--active"><a href="#"></a></li>
        <li class="menu__item"><a href="#"></a></li>
    </div>
</div>

別急,第一句你心理想的一定是一些髒話,這什麼鬼東西,這麼長?

我們了解了 BEM 的設計原則之後,你會發現這些東西一目了然,而且更具維護性與邏輯性,就像在 CSS style 裡面寫了 logic

.block{} //區塊 (Block)
.block__element{} //元素 (Element)
.block--modifier{}//修飾符(Modifier)

區塊

block.png

一般網頁設計師在設計網站的時候,會把比較大的部分切出來,是一整個區塊,比方說

  • 搜尋欄的 search block
  • 用戶登入的 auth block
  • 網頁 logo 的 logo block
  • 導航欄的 menu block

然後包住以上所有的 block 的大區塊, head block

元素

2014101900380154429769d7e69_resize.png

在區塊下的元素,如果說這些 element 是綁定在這個 block 下方,在這個設計原則規範裡面就會用雙下底線來標記他是元素,例如 menu__item

修飾符

當元素或是區塊的狀態改變了,比方說我們最常見的 active ,在設計原則裡我們會用 double dash 來連接,例如 menu__item--active

所以當一個 menu item 發生狀態變化後,他的 class 會是這樣的

<div class="menu__item menu__item--active">

這樣一來雖然看起來很冗長(但又不是你在看XD,是瀏覽器啊啊),不過卻可以清楚知道這個 active 是修飾符,同時依賴在 menu__item 這個元素下層

如此一來我們只要透過 double 底線,或是 double dash 就可以知道這個 class 裡面擁有什麼行為或階層上的邏輯

然而修飾符能夠使用的時機並不少,除了狀態更動以外,也可以用在樣式設計上

比方說原本滿版的 post 在這個頁面變成 13 就可以這樣寫 post post--1of3

看看範例

<form class="site-search  site-search--full">
    <input type="text" class="site-search__field">
    <input type="Submit" class="site-search__button" value ="Search" >
</form>

耶,知道 BEM 設計原則後,我能透過這個例子知道

  • 一個區塊 site-search
  • 兩個元素 site-search__fieldsite-search__button
  • 一個修飾符 site-search--full

Jesus Fuck !! 怎麼這麼明瞭

那麼再來一個例子

<div class="media">
    <img src="logo.png" alt="Foo Corp logo" class="img-rev">
    <div class="body">
        <h3 class="alpha">Welcome to Foo Corp</h3>
        <p class="lede">Foo Corp is the best, seriously!</p>
    </div>
</div>

嗯我不知道誰與誰有關聯,可能要去讀 source code,也不知道什麼可以複用

然我將同樣的 code 套上了 BEM 設計原則就會如下

<div class="media">
    <img src="logo.png" alt="Foo Corp logo" class="media__img--rev">
    <div class="media__body">
        <h3 class="alpha">Welcome to Foo Corp</h3>
        <p class="lede">Foo Corp is the best, seriously!</p>
    </div>
</div>

Jesus Fuck again !!!

我馬上又清楚了

  • 有一個 .media 區塊
  • 有一個元素 media__body
  • 有修飾符 media__img--rev
  • h3, p 裡面的 class 是獨立可以複用的

什麼時機不會需要用到BEM

  • 可以獨立且具高複用性

例如常見的

  • 置左 .pull-left
  • 置右 .pull-light
  • 清除浮動 .clearfix
  • 文字置中 text-center

因為使用率很高,而且可以用在不同的 tag 上,所以會建議把他獨立成一個 class

SASS 3.3 支援 BEM 設計模式

嗯…最令人頭疼的可能就是在 naming 部分,我必須先想到第一個 class name 在「區塊」上才有辦法繼續寫下去我的「元素」「修飾符」

而且在以往的設計模式會打非常多的字

.media{}
.media--full{}
.media__submit{}
.media__img{}
.media__img--border{}

但 sass 3.3 之後支援使用 &字符連接後,完美的提升 BEM 的實用性啊,針對以上的例子你只需要寫成

.media{
  &--full {}
  &__submit {}
  &__img {
    &--border {}
  }
}

如此一來,就不用打一堆字卻又能夠完美實現 BEM 設計原則了!!

在這裡我曾經好奇過 & 連接符,我原本以為

.father {
}
.son {
}

.father {
  &.son {
  }
}

會是一樣的結果,不過如果要做出更好維護且不打架的命名,用連接字符的差別很明顯就能夠顯現,我們舉個例子

如果你的 son 要能夠搭配不同的 father ,比方說 father-US(爸爸是美國人), father-TW(爸爸是台灣人)

那就可以先把 son 與 father 先模組化起來,然後用 BEM 的設計原則來達到客製化各種國家的爸爸與兒子產生出不撞名又好懂得 naming(也不用額外想一些奇怪的命名)

.us-father {
    .us-father__son {  # 這裡是為了易讀,其實可以直接寫 &__son
      這爸爸底下兒子會有的屬性
    }
}

.son {
  基本做兒子該有的屬性
}

也因為 son 模組化了,所以我們可以寫出這樣的 html code

<div class="us-father">
    <div class="son us-father__son"></div>
</div>

這樣的架構打好了,就有最基本的 son 可以使用,也可以再去利用各別客製化不同國家的 son ,也沒有所謂奇怪命名來達到世界和平,雖然會讓 class 命名更長,不過也將 _- 發揮到極致,變成有高度可讀性及維護性的標誌

針對 & 連接字符有更詳細的說明請至 痞客邦 Jimmy 的初探Sass,秒懂文章裡學習哦

小結

BEM 這個設計原則並沒有一定要使用,不過裡面有很多可以值得學習的地方,這些命名原則都是能夠讓團隊有更好的協作默契,不會有一堆狗屎要整理,或是根本整理不動也不敢刪,變成你要去讀懂他的個人 style ,所以可以嘗試精讀這樣的內容,來規劃出能夠複用也能讓同事一目了然的 CSS 架構唄

參考來源

comments powered by Disqus