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 は、構造を維持しながらカードの外観を変更します。
Navigation コンポーネント
アクティブ状態を .active のような別クラスではなく、Modifier として表現する Nav コンポーネントです。
Form コンポーネント
BEM を使ったフォームで、入力フィールドにエラー状態の Modifier を適用しています。エラーのスタイリングは親セレクタではなく、命名によって要素にスコープされています。
よくあるミス
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 を必要としません。 フロントエンドフレームワークを使って新しいプロジェクトを始めるなら、ほぼ確実にもっと良いスコープの手段が利用できます。
BEM に取って代わったもの
- CSS Modules → デフォルトでローカルスコープ。命名規約は不要
- Vue / Svelte のスコープスタイル →
<style scoped>が衝突防止を自動的に処理する - Tailwind CSS → クラスに名前をつける必要がなく、ユーティリティを直接組み合わせる
- CSS-in-JS → コンポーネントレベルのスコープが組み込み済み
BEM が今も有効な場面
- ビルドツールなし — グローバルスタイルシートを書くプレーン HTML/CSS プロジェクト
- レガシーコードベース — より良いアーキテクチャに向けて段階的にリファクタリングしている場合
- フレームワーク合意なしの複数チーム — ツールの選定でチームが合意できない場合に、共通の命名規約で衝突を防ぐ
- サーバーレンダリングされたアプリ — スタイルをスコープするビルドステップなしで HTML と CSS が別々に配信される場合
基礎知識としての BEM
本番で BEM を一切書かなくても、理解する価値はあります。CSS Modules やスコープスタイルがなぜ存在するのか、どんな問題を解決しているのかを説明してくれます。フラットなセレクタ、単一クラスの詳細度、コンポーネントスコープの命名といった概念は、現代のツールがスタイルをどう考えるかにそのまま引き継がれています。