プロジェクトに導入してみたらCSSに秩序とストレスフリーな環境をもたらしてくれたBEMという設計パターンがとてもよかったので紹介します。
CSS
みんな知ってるCSSのおさらい。 CSSはHTML要素などを指定して記述することで文字の色や背景色、大きさ、形を変更することができる宣言型の言語です。この性質上、Webサイトには絶対にCSSが何かしら書かれています。
.text { color: blue; }
例えばこのように記載することでtextというクラスの付いている要素の文字色は青色になります。とても直感的でわかりやすいですね。
とはいえ、RailsやPHPなどの所謂バックエンド領域をメインで担当してきたエンジニアの方の中にはCSSに対してあまりいい思いを抱いてはいないかもしれません。
自由度の高さによって命名に悩んだり、その場しのぎのそれっぽい記載を積み重ねた結果、ある日ボタンを一個追加しただけで全体のレイアウトが崩れるようなCSSが生まれていることもあるでしょう。気づいた時にはもう遅い。それがCSSです。
どんなやり方でも書くことができる無法地帯、オープンワールドCSSですが、上記のような悲劇を防ぐ方法の一つに今回紹介するBEMなどの設計パターンが存在します。
良いCSSとは何か
BEMという設計手法を本格的に学ぶ前に、前提としてゴールを確認しておきましょう。「良いCSS」としてGoogleのフィリップ ・ウォルトン氏のブログ、CSS Architecture — Philip Walton から引用すると以下の4つの特徴を引用します。 以下の特徴を備えたCSSは良いCSSであると言えます。
- 予測しやすいこと - 再利用しやすいこと - 保守しやすいこと - 拡張性が高いこと
簡単に意味をおさらいすると、
予測しやすいこと
cssのクラス名から期待した動作になる、挙動が名前からイメージできることです。また、他のルールと競合しないことも予測のしやすさに影響を与えます。
再利用しやすいこと
似たようなスタイルを持つクラスを再現なく増やすのではなく、抽象化したスタイルを組み合わせて新しいデザインを構築できるようにすることです。
保守しやすいこと
ここでの保守のしやすさというのは、新しいデザイン、規則を追加する際に、元々あるCSSのルールに手を加えないということを指します。新しく作ったルールが元のルールに影響を与えてしまうのは終わりの始まりと言えます。
拡張性が高いこと
拡張性の高さは保守のしやすさにも通じるものがありますが、より大きな枠として、あるCSS設計に沿って作成した結果、規模が大きくなってもルールが変わらず機能すること、他の開発者が途中から参加しても、CSSを見ればルールを読み解くことができる状態であることが望まれます。中途参加するエンジニアに対して説明の時間をとるくらいならいい感じにCSSをかけるようにした方が有益ですね。
BEM
BEM公式 前項の「良いCSS」を実現するためのアプローチの手段の1つとしてBEMという設計手法があります。ロシアのYandexというポータルを提供するFrontエンドのチームが考案した手法です。
BEMという名前は Block, Element, Modifierという3つの単語の頭文字をとったものです。また、これこそがBEMの考え方でもあります。具体的にはWebページを構成する要素をこの3つに分類して効率よく開発していくという手法になっています。
(BEMを採用する場合はSCSSやSASSなどを採用するとメリットをより享受できます。)
Block
Blockはある要素の塊のことを指します。上記画像の例を挙げると、メニューバーというBlockであるという区分になります。BEMの考え方においてWebページはBlockの集まりであると考えることができます。
丸で囲まれてるのがBlockのパターンの一例です。
Element
ElementはBlockを構成するメインの要素であるということができます。 BEMの命名規則としてはBlock名にアンダースコア を2つ付け、Elementの名前をつけるという形式になっています。
Block名__Element名
Modifier
Modifierは単語の意味の通り「修飾子」という役割を担います。Block、Elementに対するバリエーションの違いを表すために使用される用途です。Modifierの場合はアンダースコア 一つ、あるいは一目でElementとの違いを認識するためにハイフン2つを使用する手法を用います。
個人的にはアンダースコア 一個よりもこちらの方が分かりやすくて好きなので、この記事ではハイフンを2つつける方法で進めていきます。
(参考:MindBEMding – getting your head ’round BEM syntax – CSS Wizardry – Web Performance Optimisation )
Element名--Modifier名
より具体的にフラッシュメッセージ/アラートメッセージを使って命名してみましょう。
タイトルを記入するTodoアプリがあるとして、その入力が必須だった場合に記入せずに送信ボタンを押すと、こういった内容のアラートが出てくる仕様はWebサービスとして一般的なパターンと言えます。
この時html構造はこのような形になっていると想定してください。
<div class="alert alert-warning"> <p> タスクの登録に失敗しました </p> <div> <p> タイトルは必須項目です</p> </div> </div>
BEMの考え方に沿っていくと、上記のalertというクラス部分がBlockに該当します。このalertというBlockを基準にElement, Modifierを命名していきます。 Elementはこの場合、
<p> タスクの登録に失敗しました </p> <div> <p> タイトルは必須項目です</p> </div>
の2つがElementであると言えます。
<p> タスクの登録に失敗しました </p>
はalertのタイトルであるため、elementの命名に従い、alert__title
というクラスを付与します。
<div> <p> タイトルは必須項目です</p> </div>
こちらは具体的な内容になっていますね。これはalert__body
と命名しておきます。
Modifierに関してはalert-warning
の部分に改善の余地があります。今回のwarningという部分が表したいのは黄色の背景色を表示させる目的でつけています。バリエーションとしては、成功した時、致命的なエラーだった時などのパターンとして、alert-success
alert-danger
などが考えられます。ここはModifierであることを表すために、
alert--warning
としておきます。 このように書き換えていくと最終的に下記の記述になりました。
<div class="alert alert--warning"> <p class="alert__title"> タスクの登録に失敗しました </p> <div class="alert__body"> <p> タイトルは必須項目です</p> </div> </div>
ぱっと見クラス名が長くなってしまい、冗長感がありますが、それを補って余りあるメリットを享受することができます。
- block名をネームスペースとして扱うことによって、影響範囲を限定することができる。
- クラス名がの何を担当しているものなのかを容易に推察できる
- Element,Modifierの命名規則によって命名に悩むことが少なくなる。
- 命名規則によってクラスの担当しているCSSが自然と抽象的になる
更に良いCSSにするために
BEMの命名規則に従うことで恩恵を享受できることは理解できたかと思いますが、更に良いCSSにするためにいくつかルールを追加しておくと更にマネジメントしやすくなるかもしれません。そういったルールをいくつか紹介します。
以下のルールは絶対的なものではないので、何を採用するかは現状の構成や、既存のルール、採用しているフレームワークと相談してください。(本記事はRailsを用いてWeb開発を行う場合を想定しています。)
HTML要素と切り離すこと
ここまで読んでくれた方はもうわかっていると思いますが、せっかくBEMを採用しても、Blockとなる要素にHTMLタグを採用するのはNGです。 例えばBlock要素にtableを指定してしまったりすると最早何がしたいのかよく分からなくなってしまいます。
また、こちらはBlock要素以外にも同じことが言えますが、できるだけクラスセレクタを使いましょう。IDセレクタも基本的には使用しないようにしましょう。
ファイルの命名規則
SASS(SCSS)ファイルに実際のスタイルを記載していく場合、Block名.sa(c)ss
とする。つまり、一Blockに対して一つのスタイルファイルが存在するということになります。ファイル名 == Block名 のルールを徹底することによって、Block名重複というリスクを減らすことができます。
BEMではBlock in Blockという書き方(あるBlock要素の中に別のBlock要素が存在する状態)はOKです。ただし、そのような構造の場合でもスタイル用のファイルはBlock要素ごとに用意してください。
Element要素は必ずBlockが存在する場所でのみ使用する
こちらも当たり前と言えばそれまでなのですが、Block要素の中でのみ、Element要素を記載するようにしてください。例えば
<div class="block"> <p class="block__title">タイトル</p> </div>
はOKですが、
<div> <p class="block__title">タイトル</p> </div> <div class="block"> . . . </div>
のように親となるBlock要素の外側でElement要素を単独で使用するのはNGです。
BlockをまたいでElement要素を用いない
Block要素の中にBlock要素を使用するのは問題ないですが、別Block要素を挟んでその中に最初のBlock要素に関連したElement要素を入れるのはNGです。
<div class="block-1"> <div class="block-2"> <p class="block-1__element"> ブロック1のエレメントです </p> </div> </div>
このような書き方はNG。Elementの最短マッチする親Block要素は直系のものであるように書くようにするルールです。
Block要素に直接Modifier要素をつけない
<div class="alert alert--warning"> <p class="alert__title"> タスクの登録に失敗しました </p> <div class="alert__body"> <p> タイトルは必須項目です</p> </div> </div>
具体例で出した下記のalert--warning
と矛盾してしまうが、ルールとして採用している場合もあるので紹介。
Block要素に対してModifier要素はつけてはいけないというルールです。Block要素にパターンが増えた場合、ファイルの分け方の問題や、スタイルシートの書き方が複雑化するのでこういったルールを採用しているところもあったりします。
※BEM自体にはBlock要素にModifierをつけてはいけないというルールはないので注意。
Block要素に当てるスタイルは外部に影響のないものに限定する
Block要素は「再利用可能であること」を担保したいので、Block要素よりも外の範囲に影響を与えるようなスタイルは記載しないというルールです。例えばblock要素に対してmarginなどを設定すると再利用時に影響が出るので、このルールを採用するのも良いでしょう。
まとめ
自由度の高いCSSにも何かしらの設計手法、ルールを採用することで複数人の関わる開発であっても、可読性や保守性を高めることができます。今回はメジャーな手法としてBEMを紹介しましたが、SMACSSやOOCSS,MCSS,FLOCSSなど様々な手法が存在するので自分に合ったもの、プロジェクトにマッチするものを選んでみると良いと思います!
参考
CSS Architecture — Philip Walton
MindBEMding – getting your head ’round BEM syntax – CSS Wizardry – Web Performance Optimisation