明確な目的があり、親ブロックの外で実装を独立して担えるBEM要素は、独立させましょう。クラス名を変更したりCSSを移動したりする前に、以下のMacでのローカル演習を使って境界を確かめてください。
要素が独立しているとは?
HTMLを手書きしたページを保守する人にとって、実務上の問いは、成長しつつあるセクションを親の一部として保つべきか、別個に保守するブロックにすべきかです。BEMでは、ブロックを論理的にも機能的にも独立したコンポーネントと定義します。要素はブロックに属し、それ単独では使えません。ブロックにはほかのブロックを含められます。主要概念
workbenchブロックを含む架空のページを考えてみましょう。そのworkbench__previewには、抽象的な図形、ラベル、キャプションがあります。最初は、この3つはすべてワークベンチ専用です。後になって、同じプレビューを幅の狭いshelf領域でも使う必要が生じます。この2つ目の配置によって、具体的な切り出し課題が生まれます。プレビューを、独自の見た目と内部構造を持つshape-tileブロックにできるか判断しましょう。
階層の深さだけでは判断できません。BEMでは、DOM上に要素をネストしながら、名前はフラットに保てます。たとえばworkbench__preview、workbench__label、workbench__captionです。workbench__preview__captionのような名前は、ある要素が別の要素に属するという誤った表現になります。公式クイックスタートでは、複雑な要素の内部パーツを独立して整理する必要がある場合に、サービスブロックを選択肢として挙げています。クイックスタート
読みやすさのためにマークアップを別のテンプレートに移すことと、新しいBEMブロックを設けることは区別するとよいでしょう。前者は、後者をすぐに決めなくても行えます。切り出した部分が何を担い、ホストが何を提供し、別の場所でどう使うのかを説明できるようになったら、ブロックの境界を明確にしましょう。
切り出す前に依存関係を確認する
以下の判断材料は、BEMの独立性の原則に基づく編集上の方法です。公式の採点方式ではありません。ページ全体を一度に評価するのではなく、候補となるサブツリーを1つ選んで適用してください。
| 質問 | 切り出しを支持する根拠 | 要素のままにする理由 |
|---|---|---|
| 明確な目的があるか? | 「図形、ラベル、キャプションを表示する」 | 目的が「ワークベンチ内部のラッパー」だけ |
| 内部の見た目を自分で担えるか? | 枠線、余白、タイポグラフィが一体となって移動する | それらのルールが親の構造と切り離せない |
| ホストが配置を別途提供できるか? | ホストが使用可能なスペースと周囲の間隔を制御する | 候補が特定の祖先のレイアウトを必要とする |
| バリエーションは自身の特性を表しているか? | コンパクトなタイルが複数の配置で意味を持つ | どのバリエーションも特定の親の名前で呼ばれる |
| 今、別個に保守すると役立つか? | 2つ目の配置があるか、内部の責任が相当に大きい | 現時点で使い道のないインターフェースが増える |
コードを変更する前に、次のような一文を書きましょう。「shape-tileは図形、ラベル、キャプション、内部の余白を担い、ホストは配置と使用可能な幅を担う。」この文にワークベンチの列の配置や兄弟要素の順序まで含める必要があるなら、境界をどこに置くか再検討してください。
外側のレイアウトと内部の見た目は分けて考えましょう。BEMのCSSガイドラインでは、外側の寸法と配置を親に割り当て、同じノードに独立したブロックのクラスと親要素のクラスを組み合わせる例を示しています。この組み合わせはミックスと呼ばれます。CSSの方法論
たとえば、class="shape-tile workbench__preview"は両方の役割を表せます。shape-tileで枠線と内部のレイアウトを定義し、workbench__previewで配置を定義します。意図した契約の一部として上書きするのでない限り、同じプロパティを両方の役割で指定するのは避けてください。
Macでローカル比較を作る
この演習用に新しいフォルダを作成し、既存のページを変更せずに、切り出した設計を比較できるようにします。必要なのはプレーンテキストエディタとデスクトップブラウザだけです。この例では、パッケージのインストール、JavaScript、ダウンロードしたフォントや画像ファイルは必要ありません。
TextEditでFile > Newを選び、次にFormat > Make Plain Textを選択します。以下のHTMLを貼り付けてindex.htmlとして保存し、確認画面が表示されたらUse .htmlを選びます。Appleはこれらの手順のほか、Ignore rich text commandsを使ってHTMLをソースとして再度開く方法も説明しています。MacのTextEditでHTML書類を扱う
HTML文書全体を作成する
この文書では、同じコンポーネント案を2つのホストに配置します。ラベルはすべて架空のもので、抽象的な図形はCSSで描画します。以下をindex.htmlとして保存してください。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Shape tile boundary exercise</title>
<link rel="stylesheet" href="styles.css">
</head>
<body class="demo">
<h1>Shape tile boundary exercise</h1>
<main class="demo__regions">
<section class="workbench">
<h2>Workbench A</h2>
<div class="shape-tile workbench__preview">
<span class="shape-tile__shape" aria-hidden="true"></span>
<div class="shape-tile__content">
<h3 class="shape-tile__label">Form A</h3>
<p class="shape-tile__caption">Rounded sample, first arrangement.</p>
</div>
</div>
</section>
<section class="shelf">
<h2>Shelf B</h2>
<div class="shape-tile shelf__preview">
<span class="shape-tile__shape" aria-hidden="true"></span>
<div class="shape-tile__content">
<h3 class="shape-tile__label">Form B</h3>
<p class="shape-tile__caption">Rounded sample, second arrangement.</p>
</div>
</div>
</section>
</main>
</body>
</html>
スタイルシート全体を追加する
同じフォルダにstyles.cssという別のプレーンテキストファイルを作成します。実際のファイル名がstyles.cssであることを確認してください。styles.css.txtではありません。以下のスタイルシート全体を貼り付けます。
/* Page and host styles */
.demo {
margin: 0 auto;
padding: 24px;
max-width: 960px;
font-family: system-ui, sans-serif;
background: #f1f3f5;
color: #243047;
}
.demo__regions {
display: flex;
flex-wrap: wrap;
gap: 24px;
}
.workbench,
.shelf {
box-sizing: border-box;
padding: 16px;
max-width: 100%;
border: 1px dashed #6b778a;
}
.workbench { width: 480px; }
.shelf { width: 256px; }
.workbench__preview { margin-top: 24px; }
.shelf__preview { margin-top: 12px; }
/* Independent component styles */
.shape-tile {
box-sizing: border-box;
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 12px;
padding: 16px;
border: 1px solid #718096;
border-radius: 12px;
background: #fff;
color: #243047;
font: 16px/1.5 system-ui, sans-serif;
}
.shape-tile__shape {
flex: 0 0 48px;
height: 48px;
border-radius: 16px 24px 12px 24px;
background: #536dbe;
}
.shape-tile__content {
flex: 1 1 160px;
min-width: 0;
overflow-wrap: anywhere;
}
.shape-tile__label {
margin: 0;
font-size: 18px;
line-height: 1.3;
}
.shape-tile__caption { margin: 6px 0 0; }
index.htmlをSafariなどのデスクトップブラウザで開きます。比較するのは、幅の広いワークベンチと幅の狭いシェルフです。どちらのタイルも共通のコンポーネントスタイルを使います。使用できるスペースによって、図形とテキストが横に並ぶか、折り返されるかが決まります。
ここではリソースの契約が重要です。rel="stylesheet"は外部スタイルシートのリソースを指定し、href="styles.css"はこの文書が必要とするファイルを示します。BEMクラスを追加しても、そのリソースへのリンクは作られません。この演習を移動するときは、2つのファイルを一緒に保管してください。HTML Standardはこのスタイルシートとの関係を定義しています。HTML Standard: リンクタイプ「stylesheet」
切り出しがうまくいったか確かめるには?
この比較を診断演習として使います。以下に示す想定結果は、提示したコードから導かれるもので、ブラウザで確認するための項目です。ブラウザでのテスト結果を報告するものではありません。
- 基準状態を確認する。 どちらのタイルにも白い背景、青く角丸の図形、ラベル、キャプションが表示されるはずです。すべてスタイルなしで表示される場合は、コンポーネントの境界を評価する前にスタイルシートのファイル名とリンクを確認してください。
- 配置クラスを1つ削除する。 最初のタイルから
workbench__previewを削除して保存し、再読み込みします。内部の見た目は変わらず、上部の余白が変化するはずです。 - ホストクラスを1つ削除する。 親セクションから
workbenchを一時的に削除します。ホストの枠と固定幅がなくなり、タイルにはコンポーネントのスタイルが残るはずです。 - 共通コンポーネントを変更する。
.shape-tileの角丸を変更します。どちらのホストセレクターも編集せずに、両方のインスタンスが変わるはずです。 - コンテンツで負荷をかける。 片方のキャプションを、より長い架空の文に置き換えてブラウザの幅を狭めます。テキストの折り返しと、すべてのテキストが見えるかを確認します。
問題は責任範囲に照らして解釈しましょう。ホストクラスを削除した後に枠線が消えるなら、コンポーネントのスタイルがまだホストセレクターに属している可能性があります。周囲の余白が予期せず変わる場合は、配置ルールを確認してください。テキストの違いから、継承されたスタイルやグローバルセレクターが見つかることもあります。別の上書きを追加する前に、原因となる依存関係を修正してください。
BEMの命名規則によって、ブラウザが強制するスタイル分離が生まれるわけではありません。CSSの継承は引き続き文書ツリーを通じて行われます。カスケードで要素の値が指定されていない場合、継承されるプロパティは親から値を受け取ります。これはCSS Cascading and Inheritance仕様で定義され、MDNでも解説されています。CSS Cascading and Inheritance Module Level 5、7.2節、継承
この例では、意図的にタイルのルートでフォントと文字色を指定しています。実際のページでは、サイトのタイポグラフィを継承するほうが望ましい場合もあります。その依存関係を記録してください。独立性とは、必要な環境を理解しやすく、移植できることです。すべてのコンポーネント内で、ブラウザの既定スタイルを一つ残らず再現する必要があるという意味ではありません。
要素のままにすべきなのはどんなとき?
候補の意味と実装が親に固有のままであれば、要素として保ちましょう。ワークベンチ専用の配置用ラッパーは、子孫要素がいくつあっても、独立した名前を付ける必要がない場合があります。新しいブロックを導入する前に、BEM名をフラットにして構成を改善してください。
一方、コンポーネントを1か所でしか使わなくても、内部の責任が相当に大きく、依存関係の境界が明確なら切り出す価値があります。再利用は判断を支える根拠として扱い、必須の利用回数にはしないでください。この演習では、2つ目の配置によって境界を確認しやすくなりますが、常に2か所で使うべきというルールを示すものではありません。
ホスト固有のバリエーションが増えていく場合も、切り出しを再検討してください。shape-tile--inside-workbenchのような名前は、配置がコンポーネントのインターフェースに組み込まれつつあることを示唆します。まず、その違いがworkbench__previewに属するかを考えましょう。コンポーネント自体の観点から説明できる違いに、コンポーネントのバリエーションを使ってください。
最新のCSSスコープ機能で判断は変わる?
@scopeルールを使うと、囲まれたセレクターが一致する範囲を制限でき、文書内のサブツリーにあるセレクターの管理に役立ちます。MDNは現在、2026年3月以降の現行ブラウザ全般で利用できるBaseline 2026として示す一方、古いデバイスやブラウザでは未対応の場合があると説明しています。すべてのMac環境が対応しているとは限りません。CSSの@scopeアットルール
この演習では通常のクラスセレクターを使い、@scopeは必要ありません。スコープ機能のないブラウザもサポートする場合は、基本的なコンポーネントのスタイルをこれらのルールに記述してください。スコープ機能を使えば境界を表現しやすくなることがありますが、コンポーネントの目的、依存関係、配置を明確に説明して所有範囲を判断する必要があります。
複雑な要素の目的と内部実装を一緒に移動でき、周囲のレイアウトをホスト側に残し、別の文脈でも結果を示せるなら、その要素を切り出しましょう。責任範囲が絡み合ったままであれば、整理できるまで要素のままにしてください。クラス名を短くするだけでは、リファクタリングは完了しません。
出典
- 主要概念 — BEM。
- クイックスタート — BEM。
- CSS — BEM。
- MacのTextEditでHTML書類を扱う — Apple Support。
- HTML Standard: リンク — WHATWG。
- CSS Cascading and Inheritance Module Level 5 — CSS Working Group、W3C、編集者草案。
- 継承 — MDN Web Docs。
- CSSの@scopeアットルール — MDN Web Docs.