実践BEM 〜BEM導入の目的と現役で使い続ける運用ルール〜

CSS設計、フロントエンド設計手法としてBEMが広く知られるようになりました。この記事では、BEM歴6年の筆者が、これまでに現場で使ってきて感じたBEM導入のメリットや、現役で使い続けている運用ルールについて解説します。

はじめに

BEMは、フロントエンド設計手法のひとつとして広く知られています。

WEB上には「BEMとは何か」「BEMの使い方」といった内容の記事が多くあります。しかしBEMの書き方を学んだだけでは、いざ実務で使おうとしても、迷ったり、運用がうまくいかないといったことがあるのではないでしょうか。

この記事では、私がBEMを使い始めてから約6年の間に使い続けてきたプラクティスについてご紹介します。すでに諸先輩方が書かれた同様の記事もいくつか存在しますが、それらに加えて、フロントエンド設計について考える材料のひとつとしていただければ幸いです。

なお、この記事は私が今後参加するプロジェクトのメンバーへの情報提供を兼ねていますので、かなり実用的な内容になるのではないかと思っております^^

BEM導入によって目指すこと

BEMを導入するメリットはさまざまですが、私が実務上で重要視しているのは以下の点です。

チームのスケーラビリティを高める

チームメンバーの増加や入れ替わりの際に、できるだけ短期間に、手間少なく戦力になってもらうためにBEMを使います。

CSS設計という文脈でよく目にするものにはBEM、OOCSS、SMACSSなどがありますが、いまはBEMが優勢ではないかと体感的には感じています。Googleトレンドによる比較からもこの感覚が正しいと言えそうです。

利用者が多いことで、参考文献の数も多くキャッチアップが楽になりますし、もともとBEMを扱えるスキルを持つ人に出会う確率も高くなります。

このため、後述する細かいルールに関してもなるべく多くの人やプロジェクトに使われていることを重視し、BEMをアレンジしたような独自ルールはなるべく使わないようにしています。

ファーストバージョンを素早く開発する

ルールを明確にすることで迷いをなくし、ファーストバージョンを素早く開発するためにBEMを使います。

WEBページは常に変化していくものです。プロジェクトの成長に伴って既存のページが変化していったり、時にはまだ完了していないプロジェクトでも変更しなければならないことがあるかもしれません。

というか、現実的には多いですよね。デザイナーとコーダーが分業していて、デザインデータとコーディングの作業が並行して進んでいて、後半のページのデザインの影響で前半のページも変わっちゃったりとか。

BEMは、このような変更への対応コストをできるだけ低減することを目的としています。あとから変更があっても対応しやすいので、過剰に準備をすることもなく、いまある情報だけで必要なものをスピーディーに開発できます。

「変更、ドーンと来い」です。(安易な変更を推奨するわけではありません)

BEMのメリットの中で個人的にあまり重視していないもの

パフォーマンスが良い(BEMは速い)

「クラスの入れ子による指定」や「要素セレクターによる指定」と比べて、BEM的と言われる単一要素に単一クラスを指定する方法は、レンダリングが速いという説があります。これはブラウザとCSSの読み込み・レンダリングの仕様の都合のようです。

こちらを実際に検証し、結果を公開されている方々がいらっしゃいますが、結論としては実装レベルではBEMを使っても表示速度にほとんど影響はないようです。

https://twitter.com/clockmaker/status/1153314178754420738

WEB高速化のためにできる限りのことはしたいところではありますが、たとえば非BEMのサイトがあって、パフォーマンス改善を目的としてそのサイトにBEMを取り入れるということはしないでしょう。もっとコスパの良い手段を検討すると思います。

コードの再利用性

CSSによる見た目やJavaScriptによる振る舞いがブロック単位で独立して設計されていることで、他のプロジェクトなどへの再利用が容易になると言われていますが、個人的にはあまり恩恵を受けていません。

ブロックの大小によらず、あるブロックのコードを他の箇所でそっくりそのまま使えるというケースは少なく、余分なコードを移行してしまうことでメンテナンス性を下げてしまう場合もあるかもしれません。

実際の現場でBEMを使う

ブロックをどのように切るか

ブロックの切り方はプロジェクトによって最適解が異なりますが、以下の観点でブロックを設計するのが良い結果に繋がることが多いです。

  1. それ単体で見た目が成立していて、かつ役割が説明できるか
  2. どの場所で使われても同じスタイルのまま使えるか
  3. ページ内で繰り返し使われているか

1. それ単体で見た目が成立していて、かつ役割が説明できるか

たとえば、以下のような入力欄をひとつのブロックと考えてみます。

1

見た目は成立していますが、これの役割を説明するのは少し難しそうです。この場合、ブロックの切り方が細かすぎるかもしれないので、もう少し大きな単位をブロックとしてみます。

2

これなら、このブロックの役割は「過去記事を検索すること」と説明できそうで、ボタンを押すと入力欄の文字列によって検索をかけるというJavaScriptの動きも関連づけて管理できそうです。ブロックの名前はarticleSearchなどとしておくと名前からも役割や機能を想像しやすくなります。

2. どの場所で使われても同じスタイルのまま使えるか

上記の記事検索ブロックのようなものが、ページ内のヘッダーとサイドバーに利用されている場合を考えてみます。

ヘッダー、サイドバーのどちらにいても同じスタイルのまま使える場合、これらは1つのブロックとして管理するのが良いでしょう。

ただし、見た目はほぼ同じだけど、若干スタイルが違うという場合もよくあると思います。例として、以下の場合を考えてみます。

3

ヘッダーの中にいるときは横幅250px、サイドバーの中にいるときは横幅100%、といった形です。

これが設計上「記事検索ブロックは横幅に関するバリエーションをいくつか持っていて、ヘッダーとサイドバーで別々のバリエーションを使用している」というものであれば、これらを同一ブロックとして違いをモディファイアで記述するのが良いでしょう。

モディファイアを使う例
.articleSearch {
  width: 100%;
  (省略)
}

.articleSearch--widthFixed {
  width: 250px;
}

4

しかし、もしこれらがひとつのブロックのバリエーションではなく、別の役割を持つという設計であれば、現状たまたま見た目が似ているだけであってもブロックを分ける必要があります。たとえば、headerArticleSearchsideArticleSearchような名前の2つのブロックに分けます。

ブロックを分ける例
.headerArticleSearch {
  width: 250px;
  (省略)
}

.sideArticleSearch {
  width: 100%;
  (省略)
}

以下のように、他のブロックから影響を受ける形のスタイル指定は基本的にNGです。

NGな例
.articleSearch {
  width: 100%;
  (省略)
}

.headerBar .articleSearch {
  width: 250px;
  (省略)
}

3. ページ内で繰り返し使われているか

あるブロックの中でエレメントとして作っていたのと同じ見た目のものが、ブロックの外にも出てきた場合について考えます。

5

本来は、デザインデータを受け取った時点で全体を眺めて、こういったものを洗い出しておくのが良いです。しかし、デザインとコーディングが並行で進んでいるような案件ではこのように途中でブロック設計がうまくいかなくなることも少なくありません。

この場合は、もとのブロックの設計を変更し、

  • 他のブロックを入れることができるエリアを作る
  • もともと入っていたエレメントをブロックとして切り出し、このエリアに入れ直す

という作業を行います。

6

ブロックの中に別のブロックが入ることは問題ありません。その場合、お互いのブロックのスタイルが影響しないように、他のブロックを入れるエリアのdivを用意するなどの注意する必要があります。

クラス名のルール

クラス名(ブロック名)はb-で始める

名前空間的に、クラス名の先頭に必ずb-(blockのb)をつけます。

// 例
.b-text {}
.b-userProfileImage{}

記事前半で「オレオレBEMはなるべく使わない」と書いたことに矛盾しますが、これは完全に私の独自ルールです。後述する「具体的な名前にする」というルールとも若干矛盾します。私のCSS設計の師匠的な方がこのルールを使っていて、メリットがあると私も感じているので今でも使い続けています。

クラス名にはなるべく具体的なものを付けるよう心がけていますが、1ページ完結のLPなど小規模サイトでは、textとかtableと名付けるほかないようなブロックに出会ってしまうことがあります。そのときに、.text.tableなどとクラス名を付けるのは、どこかで何かと衝突するがして、激しく抵抗があります。

そこで先頭にb-をつけて.b-text.b-tableとすることで、他の何かと衝突する可能性を限りなく下げることができるので、クラスの命名に悩む時間を減らすことができます。

実際には.b-priceTableなどと具体的な名前をつけられる場合がほとんどですが、どうしても良い名前が思いつかなくても、最終的に.b-tableという名前を使う余地があるというだけで心理的にだいぶ楽になる効果があると感じています。

ただし、このb-はよくある名前空間の用途のような、たとえばtop-とかmain-のように何かの役割を持っているものではなく、BEM設計上のブロックを表すためにしか使用しません。そのため、すべてのブロック名の先頭に一律b-が付くことになり、冗長であることは否めません。

セパレーターは__--を使う

// NG
.b-gnavi_item_state_current {}

// OK
.b-gnavi__item--stateCurrent {}

ブロックとエレメントの間は__(アンダースコア2つ)、モディファイアの前は--(ハイフン2つ)を使用します。

一目でBEMのルールに則った命名がされていることが読み取れるためです。

また、感覚値ではありますが、__--で区切るのがもっとも多く利用されている印象です。

単語の区切りにはキャメルケースを使う

// NG
.b-user-profile-card__user-name {}

// OK
.b-userProfileCard__userName {}

b-のルールによってハイフンを使用していることもあり、視認性の観点で単語間の区切りにはキャメルケースを使用します。

先述のセパレーターのルールと合わせて、ブロック・エレメント・モディファイアの区切りが視覚的にわかりやすいと考えています。

単語は短縮しない

// NG
.b-usrProfImg {}

// OK
.b-userProfileImage {}

短縮して数バイトを省略するメリットよりも、ブロックの役割がわかりにくくなるデメリットの方が大きいと考えます。

長い文字数を入力する手間も、エディターの補完機能でカバーできます。

具体的な名前にする

// NG
.b-image {}

// OK
.b-userProfileImage {}

そのブロックの役割を説明する、できるだけ具体的な名前を付けるようにします。

仮にある画像に.b-imageなどといった名前を付けていて、後にデザインの異なる別の画像ブロックが追加され.b-image2などと名前をつけなければならなくなったとき、2つのブロックの役割の違いをクラス名から読み取ることは難しくなります。

終わりに

以上で、BEM導入の目的や運用上のルールの解説を終わります。

実際のプロジェクトでこれらを運用していくためには、専用の開発環境の構築が必須ですが、この記事が長くなってしまったため別途記事にしたいと思います。

近年、ReactのCSS-ModulesやVue.jsのScoped CSSの登場により、BEMなどのルールがなくても比較的安全にCSSが書けるようになってきています。しかし、WEB制作の現場ではReactやVueが導入できない環境も多く、BEMをはじめとした設計はまだまだ利用価値があると思っています。

今後も実際の現場で使いながら、またチームメンバーとの対話を通じて、自分のルールをより良いものにアップデートしていきたいと思います。

最後までお読みいただきありがとうございました。