フロントエンドの設計方法「BEM」について
BEMとは、Block、Element、Modifierの略語。Webサイトのコンポーネント化のためのフロントエンド設計方法のひとつで、厳格なclass名の命名ルールが特徴的な手法。
BEMの必要性
HTMlとCSSを使うことでしか、Webサイトを作ることができないが、HTMLとCSSにはプログラム的な機能が備わってない。そのため下記のようなトラブルすることがある。
スタイルの優先順位のコントロール不能状態
id属性を使ったスタイル指定や、深すぎる入れ子は、スタイルの優先順位のコントロールが難しくなる。そこで、タブーとされる「!important」の多用を誘発することとなり、その結果、さらに優先度のコントロールが難しくなってしまう。
凡用的に使えないスタイル
HTMLの要素セレクタに直接スタイルが指定されていると。例えば、同じの見た目のまま見出しレベルだけを変えてスタイルを流用したい場合や、メニューの現在地だけリンクを外しつつスタイルは維持したいという場合に、CSSに手を加える必要がある・
.box h2 {
border: 3px solid black;
}
上記のような指定をしていると、同じのスタイルのまま見出しレベルをh3にしたい場合、「.box h2, .box h3 { ... }」のようにセレクタを追加しなければならない。さらに、同じ.box内に別のデザインの見出しを追加したい場合、「border: none;」などでスタイルを打ち消さなければならない。
もし要素セレクタではなく、class属性を使ってスタイルを指定していれば、このような問題は起こらない。
パーツを別の場所に流用すると表示が崩れる
パーツ単体で独立しておらず、別のパーツに依存したスタイル指定になっていると、パーツの場所に移動した場合に、表示が崩れることがある。
.entry .title {
border: 3px solid black;
}
例えば、上記のCSSの場合、「.title」だけを別の場所に流用したいけれど、流用先は「.entry-page」ではない場合、このスタイルを流用することができない。
もし、「.title」だけを独立したパーツとしていれば、どこにでも流用できる。
制作者本人しかわからないclass名の命名ルール
class名の命名ルールが決まってないと、他の人がそのHTMLを見たときに、どこからどこまでがひとつのパーツのまとまりなのかわからない。
パーツの流用時、JavaScriptが動かない
JavaScriotの挙動をセットにしてパーツ化されてないと、そのパーツを別の画面に流用したときに、該当するCSSとJavaScriptを正確にコピーできないことがある。そうすると、スタイルはきちんとあたっているけど、JavaScriptが動作しないといったトラブルが起こる可能性がある。
BEMで設計することで、問題を解決できる。
BEMで解決を目指すポイント
①長時間メンテナンスできる設計で、ファーストバージョンの開発をすばやく
開発スピードが速く、しかも長時間メンテナンスしやすい設計を実現するには、設計ルールを明確に規定することで、class名の命名ルールやファイル構造などで、個人個人が悩む時間が少なくなり、その分開発スピードが上がることが期待できる。
②チームのスケーラビリティ
③コードの再利用性
Webサイトを構成するパーツがどの程度、独立して設計されることができるかで決まる。パーツを完全に独立させることができれば、コードの再利用が容易になる。ここでの再利用とは、CSSによる見た目の話だけではなく、JavaScriptによる振る舞いも含んでいる。
BEMで設計する目的
BEMはもともと、プロジェクトの成長にともなって、既存のページが変化していくことを前提として考え出される。また変更がある場合は、その対応コストをできるだけ低減することを目的としている
BEMを構成する3つの要素
BEMは、Block、Element、Modifierという3つの概念だけ理解してしまえば、あとはclass名の命名ルールに即って記述するだけの単純な方法である
世の中に存在する多くのWebページは、ヘッダー、ナビゲーション、商品説明、フッターなどといった、パーツの集まりで構成されている。BEMでは、これらのパーツをBlockと呼ぶ。
1つのBlockは、Block自身を構成する部品のようなものを有している。例えば、検索フォームBlockなら、「入力フィールド」と「ボタン」の2つの部品で構成されている。これをElementと呼ぶ。
同じBlockであっても、通常の状態とエラー状態などのように、装飾が異なることもある。BEMでは、そうした装飾に関する調整を Modifierと呼ぶ。
Block
例えば、次のようなコードの場合、searchという名前のBlockに、inputと、buttonという2つのElementが属していることになる。
<div class="search"> ← Block
<input class="search_input" type="text"> ← Element
<input class="search_button" type="submit"> ← Element
</div>
一般的なWebページの場合、blockは何度も繰り返し出現することが想定できる。そのため、Blockを識別するためにはid属性を使わずclass属性を利用している。
Blockはどこにでも置くことができ、Blockの中にBlockを含めることも可能。ただし、CSSではBlockを入れ子にしてスタイルを指定してはいけない。
なぜなら、Blockは完全に独立し、Blockを別の場所に移動しても、単体で動作可能である必要があるから。
特定の条件によりスタイルを変更するには、後述するModifierを利用する
Element
ElementはBlockの構成要素で、そのElementが属するBlock内のみで意味を成す
Elementのclass名には必ず所属するBlock名を含める
.search { ... }
.search __input { ... }
.search__button { ... }
Block名を含めるメリット
・CSS上でセレクタを入れ子にする必要がなくなり、スタイルの優先度で頭を悩ませる必要がなくなる。
・ルールが統一されるため、HTMLを見ただけで、どこからどこまでがひとつのBlockなのかわかりやすくなる
ひとつのElementはBlock内で何度も繰り返し利用できる
Modifier
既存のBlockやElementと似ているけれど、見た目や動きが少しだけ違うものを作りたい場合には、新規にそれらを作るのではなく、Modifierを使うことができる
例えば、同じリストで、行頭記号が2種類以上存在する場合や、同じメニュー内の「タブ」であっても、現在ユーザーがいるタブだけ、見栄えを変える場合などである。
Modifierは、BlockやElementのバリエーションの軸(例:行頭記号)や、状態(例:現在地)を表すプロパティの役割をする。
Modifierは名前(key)と値(value)を持ち、複数のModifierを同時に使用することができる。
Modifierには、Blockに対するModifierと、Elementに対するModifierがある。
・BlockのModifier(リストの行頭記号が2種類ある場合)
この場合のclass名は「Block_key_value」で表す
下記のようなコードの場合、listというclass名のBlockに、typeのバリエーションがあり、それはdiscとcheckという2種類だ、ということが分かる
<ul class="list list_type_disc">
<li class="list__item"></li>
<li class="list__item"></li>
・・・
</ul>
<ul class="list list_type_check">
<li class="list__item"></li>
<li class="list__item"></li>
・・・
</ul>
ElementのModifier
同じメニュー内の「タブ」であっても、現在ユーザーがいるタブだけ、見栄えを変える場合は、タブというElementに対するModifierが必要
この場合のclass名は「Element_key_value」で表す
下記のようなコードの場合、menu_itemというclass名のElementに、state(状態)が違うものがあり、それはcurrentという現在地の状態を表すものだということが分かる
<ul class="menu">
<li class"menu_item"></li>
<li class"menu_item menu__item_state_current"></li>
<li class"menu_item"></li>
</ul>