メインコンテンツまでスキップ
  • Created:
  • Updated:
  • Author:
    Takeshi Takatsudo

BEM戦略

歴史的背景

BEM は、CSS のスコープが広く利用可能になる以前の時代に生まれた伝統的な CSS 命名規約です。他のプログラミング言語では当たり前に使える「スコープ」を、グローバルなプレーン CSS 上でシミュレートするために考案されました。

最近のプロジェクトのほとんどは BEM を必要としません。 現代のツールがスコープを自動的に処理してくれます:

  • CSS Modules — クラス名はビルド時にローカルスコープされる
  • Vue / Svelte のスコープスタイル<style scoped> がコンポーネントごとにユニークなセレクタを生成する
  • Tailwind CSS — ユーティリティファーストのアプローチにより、命名規約自体が不要になる
  • CSS-in-JS(styled-components、Emotion)— スタイルがデフォルトでコンポーネントにスコープされる

BEM は基礎知識として依然価値があります。なぜ現代のスコープソリューションが生まれ、コンポーネントの境界をどう考えるかを説明してくれます。また、現代のツールが使えない環境では今も有効です。

BEM は「今日使っているツールの背景にある考え方」を理解するためのものであり、新しいプロジェクトで採用するパターンとして手を伸ばすものではありません。

問題

CSS の命名に規約がないと、名前の衝突、詳細度(specificity)の争い、スタイル間の不明瞭な関係が生じます。複数人で開発するプロジェクトでは、チームメンバーごとに異なる命名パターンを使い、一貫性が失われます。AI エージェントは .title.container.btn-blue のような汎用的なクラス名を生成しがちで、コンポーネント間で名前が衝突しやすくなります。さらに、.sidebar .nav ul li a.active のような深くネストしたセレクタをデフォルトで使い、!important なしではオーバーライド不可能な詳細度の連鎖を作ってしまいます。

解決方法

BEM(Block Element Modifier)は、.block__element--modifier という厳格な命名規約を提供します。これにより、フラットで単一クラスのセレクタが作られ、詳細度の問題を完全に回避しつつ、名前自体でコンポーネントの構造を伝えることができます。

  • Block: 独立した再利用可能なコンポーネント(.card.nav.form
  • Element: Block の一部で、Block 名とダブルアンダースコアで接頭辞を付ける(.card__title.card__body
  • Modifier: Block または Element のバリエーションで、ダブルハイフンを末尾に付ける(.card--featured.card__title--large

すべてのセレクタが同じ詳細度(クラス1つ分)を持つため、カスケードが予測可能になり、オーバーライドも簡単です。

コード例

BEM の命名規約

.block                    → 独立したコンポーネント
.block__element → Block の子要素
.block--modifier → Block のバリエーション
.block__element--modifier → Element のバリエーション

例:

/* Block */
.card { }
.nav { }
.form { }

/* Element */
.card__title { }
.card__body { }
.card__image { }

/* Modifier */
.card--featured { }
.card__title--large { }

Card コンポーネント

BEM 命名を使った完全な Card コンポーネントです。featured Modifier は、構造を維持しながらカードの外観を変更します。

BEM: Card コンポーネント

アクティブ状態を .active のような別クラスではなく、Modifier として表現する Nav コンポーネントです。

BEM: Navigation コンポーネント

Form コンポーネント

BEM を使ったフォームで、入力フィールドにエラー状態の Modifier を適用しています。エラーのスタイリングは親セレクタではなく、命名によって要素にスコープされています。

BEM: Form コンポーネント

よくあるミス

Element を深くネストしすぎる

BEM の Element は DOM のネスト構造を反映してはいけません。Element 名はフラットにして、Block のみを参照しましょう。

/* 誤り: DOM ツリーを反映している */
.card__body__title__text { }

/* 正しい: Block へのフラットな参照 */
.card__text { }

ベースクラスなしで Modifier を使う

Modifier クラスは常にベースの Block または Element クラスとペアで使うべきです。Modifier は特定のプロパティのみをオーバーライドし、ベースクラスが基盤を提供します。

<!-- 誤り: Modifier だけを使用 -->
<div class="card--featured">...</div>

<!-- 正しい: ベース + Modifier -->
<div class="card card--featured">...</div>

BEM をレイアウトに使う

BEM はコンポーネントクラスの命名のためのものであり、ページレベルのレイアウトのためのものではありません。レイアウトの関心事には別のユーティリティクラスやレイアウトクラスを使いましょう。

<!-- 誤り: BEM をレイアウトに使用 -->
<div class="page__sidebar">...</div>

<!-- 改善: レイアウトとコンポーネント命名を分離 -->
<div class="sidebar">
<nav class="nav">...</nav>
</div>

BEM とモダン CSS

CSS ネスティング(CSS nesting)は現在すべての主要ブラウザでサポートされており、BEM をさらに使いやすくします。& セレクタを使えば、すべてのルールを Block 内に書くことができ、命名規約を維持しながら繰り返しを減らせます。

.card {
border: 1px solid #e2e8f0;
border-radius: 8px;
overflow: hidden;

&__image {
width: 100%;
display: block;
}

&__title {
font-size: 18px;
font-weight: 600;
}

&__body {
padding: 16px;
}

&--featured {
border-color: #3b82f6;
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.15);
}
}
BEM と CSS ネスティング

使い分け

最近のプロジェクトのほとんどは BEM を必要としません。 フロントエンドフレームワークを使って新しいプロジェクトを始めるなら、ほぼ確実にもっと良いスコープの手段が利用できます。

BEM に取って代わったもの

  • CSS Modules → デフォルトでローカルスコープ。命名規約は不要
  • Vue / Svelte のスコープスタイル<style scoped> が衝突防止を自動的に処理する
  • Tailwind CSS → クラスに名前をつける必要がなく、ユーティリティを直接組み合わせる
  • CSS-in-JS → コンポーネントレベルのスコープが組み込み済み

BEM が今も有効な場面

  • ビルドツールなし — グローバルスタイルシートを書くプレーン HTML/CSS プロジェクト
  • レガシーコードベース — より良いアーキテクチャに向けて段階的にリファクタリングしている場合
  • フレームワーク合意なしの複数チーム — ツールの選定でチームが合意できない場合に、共通の命名規約で衝突を防ぐ
  • サーバーレンダリングされたアプリ — スタイルをスコープするビルドステップなしで HTML と CSS が別々に配信される場合

基礎知識としての BEM

本番で BEM を一切書かなくても、理解する価値はあります。CSS Modules やスコープスタイルがなぜ存在するのか、どんな問題を解決しているのかを説明してくれます。フラットなセレクタ、単一クラスの詳細度、コンポーネントスコープの命名といった概念は、現代のツールがスタイルをどう考えるかにそのまま引き継がれています。

参考リンク