Write Cleaner CSS with BEM

如何用 BEM 撰寫更好維護的 CSS

為什麼需要 BEM ?

BEM 是一種管理 CSS 撰寫方式的一種規範。撰寫小型的網站時通常不需要特別考慮到未來樣式的命名與權級的規劃,但隨著網站的複雜度增長就必須需要一套可預測與可擴充的方式,BEM CSS🔗 由於概念簡潔好懂,成效顯著,並且有著相對長遠的歷史被測試與應用過,很適合作為初學者第一套管理 CSS 的辦法。

學習 BEM 能帶來哪些好處?

  1. 更好的 描述網頁元件的功能
  2. 更好的 交代網頁元件的架構
  3. 更好的 迴避掉選取權重問題

開始學習 BEM 之前……

開發者應當理解「語意化命名」與「模塊化」撰寫網頁的重要性,才能在此基礎上使用 BEM 更進一步的分類管理樣式。

語意化命名

<!-- 不正確,red-text 僅描述元件的外貌 -->
<div class="red-text"></div>
<!-- 正確,error 有意義的描述元件 -->
<div class="error"></div>

所有的 BEM CSS 都應該以語意化的方式來命名,如:button, form, header,而非無意義的命名或僅描述外貌與結果的命名,如:aaa, red, center,語意化的命名可以幫助開發者描述元件的用途與關係,更好的撰寫出可長期維護與擴充的代碼。

模塊化

<!-- 不正確,無法重複利用的樣式 -->
<a href="#" class="button">
<a href="#" class="button-solid">
<a href="#" class="button-outlined">
<!-- 正確,拆分重複度高的樣式賦予名稱統一管理 -->
<a href="#" class="button">
<a href="#" class="button button-solid">
<a href="#" class="button button-outlined">

網頁可以被視為一塊塊的元件 (Component),將高度重複的樣式給抽離出來。舉例來說:如果開發上有許多種類的按鈕,應當製作一個 buttonclass ,並依照樣式功能上的不同再添加 class ,像是:實心(solid)、空心(outlined)、危險(danger)、警告(warning) 而非為每個種類的按鈕都撰寫一個無法重複利用的 class

開始學習 BEM

BEM 將網頁上的任何元件看作是一塊塊功能獨立、可以被重複利用的區塊,並運用元素 來標註區塊不可分割的子項目,或運用修飾符創造不同功能樣式的區塊。聽起來非常的複雜?讓我們進入下一個章節詳細了解這三種分類的差別與用途:

  • B - 區塊 (Block)
  • E - 元素 (Element)
  • M - 修飾符 (Modifier)

區塊 Block

撰寫 BEM 先從區塊開始,實際上就和一般專寫 CSS 時沒有兩樣,要注意區塊應當避免影響環境,不同區塊之間不應該相互影響且區塊之間是對等的,並沒有一定的從屬關係。舉例來說:logo 常常出現在網站的 navbar 中,但不代表一定只能在 navbar 之中,這就是「沒有一定從屬關係」的意思,如果有從屬關係應當歸類到 元素 之中。

<!-- header 區塊 -->
<header class="header">
<!-- 嵌套的 logo 區塊 -->
<div class="logo"></div>
<!-- 嵌套的 search-form 區塊 -->
<form class="searchForm"></form>
</header>
<footer class="footer">
<!-- logo 仍可以在 footer 中出現 -->
<div class="logo"></div>
</footer>
  • 區塊之間並沒有從屬關係。
  • 區塊不應影響環境,舉例來說:不使用 marginposition 。確保區塊可以被移動並且重複使用。
  • 區塊可以被嵌套在一塊,多少層次都是可行的。

元素 Element

元素無法獨立存在,且會隸屬於一個區塊,舉例來說:navbar 是一個區塊,但內部的 lightSwitch 卻只能用在 navbar 中,因此將它定義為 navbar 的元素 navbar__lightSwitch,這個 navbar__lightSwitch 並不會在 navbar 以外的任何地方出現,這就是元素。

<!-- navbar 區塊 -->
<nav class="navbar">
<!-- navbar內的 lightSwitch 元素 -->
<button class="navbar__lightSwitch">light</button>
</nav>
  • 元素需在區塊後命名加上兩個底線(__)來表示。
  • 元素永遠隸屬於某個區塊中,無法被單獨使用。
  • 元素可以被嵌套在一塊,多少層次都是可行的。
  • 元素無法被雙重疊加,舉例:block__elem1__elem2

修飾符 Modifier

舉例來說,一個按鈕它的外貌特徵(大小 / 風格),或是狀態(啟用 / 隱藏) 都可以使用修飾符附註上去。

  • 修飾符需加上兩個連接號(–-)來表示
  • 修飾符僅描述區塊或元素被附加的樣式,無法被單獨使用。
<!-- 是 focused 狀態的 searchForm 區塊 -->
<button class="button button--large button--active"></button>

BEM 常見的問題

可以使用進階選取器嗎?

不行。 BEM 只使用 class 選取器就是為了迴避選取權級越疊越高導致難管理的問題。

好像每個人的 BEM 都長得不太一樣?

BEM 的核心概念 Block/Element/Modifier 要怎麼撰寫與區分它們,就依照個人或開發團隊喜好去改變了。本篇文章所有範例都添加了筆者的個人偏好(2 Dashes style🔗 + Camel Case Style🔗),可以參考 BEM Naming convention🔗 來了解各種範例。

我應該使用 BEM 嗎?

如果專案並沒有這麼大,只是隨手寫個 CSS 那麼可能並不需要 BEM。BEM 最被為人詬病的一點,就是過長過複雜的 class 名稱,以及失去使用各種選取器的便捷性。其他如果是是 Utility-First CSS 優先的開發模式的話,那麼從根本上就不需要 BEM ,不過這又是另一個篇章的話題了。

參考資料