こんにちは!Holmesでサーバーサイドエンジニアをしている平田です。
タイトルにもある通り、HolmesではBEMをベースにアレンジを加えて運用しています。
私は2020年1月からフロントエンドもがっつりやるようになったので、その際に勉強したBEMと自社でどのようにアレンジして運用しているかを紹介します。
BEMとは
BEMとは、より構造化されたCSS設計を行うための方法論の一つです。公式
BEMはBlock、Element、Modifierの頭文字をとったもので、”ベム”と呼びます。
BEMに則って開発することで、保守性・使用性・移植性の高いCSSコードを書くことができます。
以前、Holmesでは複数画面に渡って利用している3000行からなるCSSファイルを秘伝のタレのように継ぎ足し継ぎ足しで運用していたため、修正の際に毎回他の画面が崩れたり、!important
が乱用される状況になっていました。
Holmesでは2019年5月からBEM等を取り入れて改善を図ってきました。
BEMを導入することによって、画面固有のCSS、共通のCSSが記述・利用しやすくなったため、不要な考慮も少なく、開発スピードも向上しました。
BEMのルール
BEMはclassセレクタを利用します。
そうすることでCSSの優先度をBEMで記載した通りに統一することができます。
Block
Blockは独立した意味のある塊です。
Block名は小文字の英数字で記述し、単語はハイフンで区切ります。
- HTML
<div class="block"> ... </ div> <div class="article"> ... </ div>
.block { ... } .article { ... }
Blockは他のBlockにネストすることができます。
<div class="block1"> ... <div class="block2"> ... </ div> </ div>
Element
ElementはBlockの中に記述されるもので、Blockと紐づくことで意味をなすものです。
Element名は小文字の英数字で記述し、単語はハイフンで区切ります。Blockとは__(アンダースコア2つ)で繋げます。
Elementは単体では利用できず、Blockの入れ子として利用します。
- HTML
<div class="block"> ... <span class="block__element"> ... </ span> </ div> <div class="article"> ... <span class="article__title"> ... </ span> </ div>
.block__element { ... } .article__title { ... }
ElementのElementを記述することはできません。
- 誤
<span class="block__element__element"> ... </ span>
ただし、DOMツリー内でのネストはできます。
- 正
<div class="block"> ... <div class="block__element1"> ... <span class="block__element2"> ... </ span> </ div> </ div>
Modifier
Modifierは、BlockやElementに付随して、外観、動作、または状態を変更します。
Modifier名は小文字の英数字で記述し、単語はハイフンで区切ります。BlockやElementと_(アンダースコア1つ)で繋げます。
Modifierとしてプロパティーと値を表現する場合は、_(アンダースコア1つ)で紐づけます。color_red
また、Modifierは修飾子のため、単体では利用できず、非修飾要素(BlockやElement)とマルチクラスで記述します。
- HTML
<div class="block block_modifier"> ... <span class="block__element block__element_modifier"> ... </ span> </ div> <div class="article article_size-big"> ... <span class="article__title article__title_color_red"> ... </ span> </ div>
.block { ... } .block .block_modifier { ... } .block__element { ... } .block__element .block__element_modifier { ... } .article { ... } .article .article_size_big { ... } .article__title { ... } .article__title .article__title_color_red { ... }
ファイル構造
Blockごとにファイルを分けます。
そうすることで、意味のある塊であるBlockの重複を防ぐことができるとともに、巨大なCSSファイルが出来上がるのを抑制できます。
Holmes流にアレンジしたBEM
SCSS
SCSSはSassというスタイルシート言語の構文の一つです。
SCSSを使うことで、BEMをより書きやすくなります。
.block { ... } .block .block_modifier { ... } .block__element { ... } .block__element .block__element_modifier { ... }
- SCSS
.block { ... &_modifier { ... } &__element { ... &_modifier { ... } } }
- CSS (SCSSの変換)
.block { ... } .block_modifier { ... } .block__element { ... } .block__element_modifier { ... }
メリット
- 冗長な記述を減らせる
- 入れ子であることで親子関係がわかりやすい
デメリット
- Modifierをマルチクラスで設計するには工夫が必要
- 見難い
Holmesではこれらのメリット・デメリットを踏まえて下記のような運用を行っています。
記述ルールのアレンジ
Block
Block名は先頭が大文字、単語を繋げる際は先頭を大文字にして繋げる、アッパーキャメルケースで記述します。
理由はサードパーティのCSSとのコンフリクトを防ぐためで、開発スピードや崩れを防ぐための策です。
やってみるとHTMLでは大文字が視認し易く、よかったと思います。
.Block { ... } .BlockList { ... }
Element
ElementはBlockの要素であることを明確にするため、Block配下として記述します。
Element名は先頭が子文字、単語を繋げる際は先頭を大文字にして繋げる、ローワーキャメルケースで記述します。 Blockとは本家同様__(アンダースコア2つ)で繋げます。
記述はやや冗長になりますが、視認性の高さとclassのネスト関係のスコープを優先しています。
- HTML
<div class="block"> ... <span class="block__element"> ... </ span> </ div>
- SCSS
.block { ... .block__element { ... } }
- CSS (SCSSの変換)
.block { ... } .block .block__element { ... }
Modifier
Modifierは先頭-(ハイフン1つ)にして、文字の先頭を子文字、単語を繋げる際は先頭を大文字にして繋げる、ローワーキャメルケースで記述します。
また、BlockやElementとは繋げた冗長な記述はせず、非修飾要素(BlockやElement)とマルチクラスで記述します。CSSでマルチクラスを保証します。
- HTML
<div class="block -modifier"> ... <span class="block__element -modifier"> ... </ span> </ div>
- SCSS
.Block { ... &.-modifier { ... } .Block__element { ... &.-modifier } }
- CSS (SCSSの変換)
.Block { ... } .Block.-modifier { ... } .Block .Block__element { ... } .Block .Block__element.-modifier { ... }
運用した結果
メリット
デメリット
- Blockの分け方や命名は開発者次第になっている
- Block__elementは冗長な記述になる
- マルチクラスになった場合、HTML上でModifierがどちらを修飾しているかわからない
このModifier運用のデメリットについて
上記でデメリットとして、「マルチクラスになった場合、HTML上でModifierがどちらを修飾しているかわからない」をあげました。
調べてみると、このようにModifier名を単体で書くことには否定の意見をちらほら見かけました。
個人的な考えとしては、適切なModifierであればどちらを修飾しているかは意識しなくて構わないと思っています。
理由
- セレクタの指定で縛っているので、記述しても適切なものにしか修飾されない
- 適切に設計していれば、どちらを修飾していようと適切な振る舞いをする
解決できていない問題
改修によってマルチクラスになっている片方を削除しなければならない場合は問題となります。
Modifierが修飾している方を消した場合、Modifierがあるにも関わらず、Modifierが効かないということが起こります。
これについては、それぞれのBlock設計時にModifierを考慮していないことに問題があるのではないかと思います。そもそも共通で使用するモジュールであれば、優先的にModifierをつける等の設計工夫ができます。
HTML側では下記のように、Modifierを被修飾classの次に書くなど、ルール化することでも極力問題を減らせそうです。
class="被修飾class Modifier 他class"
HolmesではBEMを使い始めてからModifier以外をマルチクラスにならないようにしているため、問題は今の所起こっていません。
おわりに
いかがでしたでしょうか。
BEMの表面的な部分とHolmesでどのようにアレンジして運用しているかを紹介してきました。
BEMを検索してみると、オリジナルから派生した色々な記法がBEMとして語られています。
序盤に説明したBEMは公式だと思われるものを参照していますが、間違いがあれば指摘していただければ幸いです。
Holmesで利用しているアレンジもどこかで同じものがあるかもしれませんし、皆さんも環境に合ったものを考えてみてください。
Holmesはエンジニア・デザイナーを募集しています 。
興味がある方はこちらからご連絡ください!