[CSS設計] BEMの使い方を知る

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