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(モディファイア)の頭文字を取った略語である。

  • Block → 塊
  • Element → 要素
  • Modifier → 状態

クラスの命名規則もこれの概念に沿って付けていく。

  • .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開発が出来るようになると思う。