CSS 設計のために、改めて BEM について考えてみた。
BEM の基本的な記法に加えて、踏み込んで触ってみて実感した使い方やノウハウなどを記載する。
BEM とは BEM は、検索エンジンのYandex が使っている CSS の設計方法らしい(実際、Yandex のサイト内を見てみるとクラスの指定が BEM になっている 😳)
<!-- https://yandex.com/company/ - code -->
< div class = "widget" >
< div class = "widget__title" ></ div >
< div class = "widget__post" >
< a class = "widget__post-header" ></ a >
< div class = "widget__date" ></ div >
</ div >
</ div > 一般的に BEM を使った CSS 設計と言っても、主に MindBEMding が使われているようである。本家の BEM とは命名規則などのルールに違いがある。本記事では MindBEMding を前提に記載をする。
BEM の基本的な使い方 BEM は、Block(ブロック)、Element(要素)、Modifier(モディファイア)の頭文字を取った略語である。
B lock → 塊E lement → 要素M odifier → 状態クラスの命名規則もこれの概念に沿って付けていく。
.Block コンポーネントの親要素 / 独立した要素.Block__Element Block に紐付いた要素 / Block 内でいくつも存在できる.Block--Modifier / .Block__Element--Modifier バリエーションや状態を変化させるときに指定する命名規則はハイフン 2 つやアンダースコア 2 つでつなげる。親は Block であり、子に Element が続く場合は__で、Modifierが続く場合は--でつなげていく。
< div class = "block" >
< div class = "block__element" ></ div >
< div class = "block__element--modifier" ></ div >
</ div > BEM が提唱している記法にとどまらず、Block--Modifier--Modifierなどのような命名も可能ではあるが、イレギュラーケースを作りすぎると規則性がなくなっていくので、私は基本記法にとどめている。
Block Block は独立した存在で、大本の「構成要素」になる。競合しない名前空間を作り、外部から影響を受けないようにする。
< div class = "block" ></ div > 命名だけのルールであるため、当然<span>要素も Block 足り得るのだが、意識的にはいわゆる「ブロック要素」を Block とした方が実装はしやすい。
Element Element は、所属する Block の一部であり、そこでのみ意味を成す(独立した存在にはなれない)。
クラス名は.block__elementのような命名になる。 Block の名前を引き継ぎつつ、 Element の前をアンダースコア 2 つでつなぐ。
< div class = "block" >
< ul class = "block__list" >
< li class = "block__item" ></ li >
< li class = "block__item" ></ li >
< li class = "block__item" ></ li >
</ ul >
</ div > .block {
& __list {
}
& __item {
}
}
// or
.block {
}
.block__list {
}
.block__item {
} Block により名前空間が作られるので、Element が他への影響を与えることがない。
Modifier Modifier は Block と Element のバリエーションや状態の変化を表す。
クラス名は.block__element--modifier/.block--modifierのような命名になる。Block・Element に続けて、Modifier をハイフン 2 つでつなぐ。
< div class = "block" >
< ul class = "block__list--inline" >
< li class = "block__item" ></ li >
< li class = "block__item" ></ li >
< li class = "block__item" ></ li >
</ ul >
</ div > .block {
& __list {
}
& __list--inline {
}
}
// or
.block {
}
.block__list {
}
.block__list--inline {
} その他 BEM の基本的な記法は、Block・Element・Modifier 以上のものはない。ただ、BEM 記法を利用したシステムで出てくる使いづらさなどは別途回避をしていきたい。
状態の表現 SMACSS / FLOCSS などでは、状態を表すときis-*プレフィックスを使い.is-stateのような命名をマルチクラスで指定する。
BEM の場合は、そのまま Modifier で表現するのだが、JS で状態変化をさせるためにクラスを変える際は、もともとのクラスごと差し替えないといけなくなる。
// for モダンブラウザ
const block = document . querySelector ( '.block__list' );
block . classList . replace ( 'block__list' , 'block__list--state' );
// for レガシーブラウザ
block . classList . remove ( 'block__list' );
block . classList . add ( 'block__list--state' ); 元のクラス(.block)を差し替えるとなると管理が厳しくなる。本来、BEM にのっとった場合は Modifier でクラス定義するのが正しい。しかし、こういった JS を使った状態管理の場合はis-stateを許容してもよいだろう。 ただし、このマルチクラスでは Block のスタイルを継承などはせず、状態に応じた見た目の変更のみ定義すると見通しが良くなる。
// 定義もマルチクラスとして定義
.block__list {
display: block ;
& .is-state {
display: none ;
}
}
// もしくは汎用的に定義する
.is-state {
display: none ;
} ちなみに FLOCSS の思想では、Modifier のスタイルを汚染してしまうのを防ぐため、このis-state自体にスタイル定義を持たせるのを禁止している。
Element 内の Element のパターン(element_element) HTML 構造へ寄せた命名にする必要はない。
<!-- NG -->
< div class = "block" >
< ul class = "block__list" >
< li class = "block__list__item" ></ li >
</ ul >
</ div >
<!-- OK -->
< div class = "block" >
< ul class = "block__list" >
< li class = "block__item" ></ li >
</ ul >
</ div > HTML の構造に寄せたところで Block 内のネスト構造は変わる可能性もあるため、block__list__itemではなく Block 内の Element という位置付けとしてblock__itemで良い。
おわり BEM 記法は、非常にみにくい。命名から構造が分かりやすいと言っても命名者次第といったところで、単純に命名で縛る(ネームスペースの担保)ものでしかない。だが、BEM を超える合理的な命名規則が今の所ないため、現時点の最適解として受け入れられる。
FLOCSS が MindBEMding の記法をそのまま取り入れているように、プロジェクトに適した形(他の設計思想とうまく組み合わせる)で取り入れると、CSS 開発の効率を上げられる。