CSSのネーミングでもう悩まない! BEM+OOCSSのネーミングルール

BEMの利点と落とし穴

BEMは使用箇所が明確で、何に使っているのかがすぐ分かって、関連するほかのクラスもすぐに見つけられて、そのため保守性が高い。
見た目は確かに醜いけれど、それ以上に利点が大きい。使わない手はない。


とはいえ、それなりに経験を積まないとちゃんとしたネーミングができない問題がある。
よくある間違いが、HTMLの構造の通りにCSSにネーミングしてしまうこと。そこまでいかなくても、ネストが深くなりやすい。
Sassなどを使っていればコードを書く負荷は下がるけど、出力されるCSSは肥大化するし、CSSの可読性が大幅に下がる。HTMLもクラス名のせいで読みにくくなる。一回これをやらかした。


本当にこれでいいのか?


よくないに決まっている。そこで一旦BEMから離れた。しかしこれが強力な手法であることに間違いはない。

なぜ正しくないアプローチをしてしまったのか?

BEMは、Block, Element, Modifierの順で名づけを行う。デザインをモジュールで把握して、「Block__Element--Modifier」の順で名前を付ける。
日本語で書くなら、「箱__箱--装飾・状態」である。
さらにBlock用のModifierとElement用のModifierを作ることができてそれらを1つの要素に詰め込むことができる。


これだけでも結構ややこしくなる。
BlockとElementをどこで判別するのか。これはElementだけれども、見方によってはBlockになる、どうすればいい?
また、ウェブページはBlockの入れ子構造になっている。関係性を明確にするのであれば、どのBlockまでさかのぼってネーミングすべきだろうか?


もう一つ。BEMではBlock, Element, Modifierのそれぞれで、ハイフンやアンダースコアを使って単語をいくつも結合できるようになっている。
制限がないので、詳しく書ける。そうすればあとから読む人がすぐに意味を把握できる。もとよりBEMはそこを重視している。

ここに落とし穴があった。


自由度が高すぎるのだ。多すぎる選択肢で悩む。
また、長く、複雑すると名前の重複を避けることができる。利点である一方、同じスタイルを別の名前で再生産するリスクも増える。ページの追加が多いサイトなら、条件次第ではCSSが膨大になる。
これではいけない。何か制限するものが必要である。より良い方法を見つけるための他のCSS設計を調べることにした。

OOCSS、SMACSS、FLOCSS…

プログラミングになじみがないため、オブジェクト指向やプロジェクト、クラスといった用語に戸惑ったが、サイトの構造やモジュールの性質を分類・理解するための助けになった。


ネーミングについては、レイアウトにはl-接頭辞を、モジュールにはm-接頭辞を、などというのはあるのだけど(SMACCS)、微妙に両方の性質を持っているモジュールがあったりして、こちらはむしろ悩みが増えることに。

救世主はBootstrap

今まで使わない機能が9割だったので、グリッドシステムやセマンティックなネーミングを取り入れるだけだったのだけどこれが「これならいけるんじゃね?」という答えをくれた。

col-sm-6

グリッドの列分割で、スモールサイズ以上の画面幅では、6グリッド。


「…なんかこれ、BEMっぽくね?」


なんというか、旅をして帰ってきたら青い鳥がそこにいました的な。

「機能」−「適用場所」−「詳細」

なんかこれがすべてのモジュールに適用できそうな気がした。レスポンシブデザインに最適じゃないか。
実際に検討してみたらそう簡単でもなかったのだけど、以下のように整理することができた。

「役割」−「使用場所または担当範囲」−「詳細」

これを適用すると、

col-sm-6
gnav-item
btn-card-submit

てな形になる。


自由度をなくすために、単語はハイフン一つで結合し、アンダースコアやキャメルケースは禁止。これで名前が冗長にならないようにできる。(ハイフン一つの縛りはWordPressのコーディング基準と互換性を持たせるためでもある)


上記の命名ルールを英単語に置き換えると、

Role-Position-Detail

となる。頭文字を拾うとRPD。ほかの設計概念より無味乾燥な感じだけど、強引に「ラピッド」と呼ぶことにした。「RPD-CSSNamig」さっさと決めようクラス名。

さらにOOCSSの概念を導入

実はこれだけではネーミングはできても、サイトは構築できない。
Detailのところに装飾を担わせると、単語数が不足するのである。装飾のバリエーションは広いので1単語では足りない。


とはいえ、ネーミングの問題を検討していた当初から機能と装飾は完全に分離した方がよいと考えていた。
そもそもこの「ネーミングどうすべ?」問題、CSSフレームワークを自作するときに生じたもの。--Modifierでわずかに違うだけの同じスタイルを繰り返し使うと、CSSファイルが肥大化する。Sassを使えば管理は楽だが、Sassを使っていないコーダーがいる場合、編集されるのはCSSの方になる。そうすると同じスタイルが繰り返し出てくると保守性が下がる。
そのためこのフレームワークは主に構造あるいは機能に絞り、装飾は最小限にとどめるようにしていた。スタイルを上書きではなく追記することで、多くのサイトに使いまわせるようにという魂胆である。


当初はフレームワークのクラスに追記すればいいかな?と思っていたが、OOCSSのおかげで装飾専用のクラスを作る方が合理的だと考えなおした。
装飾専用のクラスを作り、モジュールとセットで運用する。


これを「従属セレクタ」と呼ぶことにした。単体ではスタイルを持たず、他のモジュールと組み合わせた時に、その装飾部分のスタイルを担当させるのである。
これなら、HTMLとCSSのどちら側でも装飾の修正や追記を簡単に行える。
また、従属セレクタの名前は、他のモジュールにも使いまわすことができる。


従属セレクタは運用が特殊になるため、他のモジュールとは明確に区別する必要がある。そこで従属セレクタにはdep-(depend)という接頭辞を付けることにした。

モジュールとセットで運用する場面があるのはJavaScriptセレクタも同様である。そこでこれも従属セレクタとし、接頭辞js-を付けることにした。IDセレクタを使う場合はjs-は要らない。


いずれも接頭辞のあとはRPDのルールを適用する。(とはいえほとんどの場合、役割名だけで済むだろう)

Baseの概念も入れておく

ここまで検討してきたのはすべて何らかの機能を持ったモジュールに関するものになる。
実際にはHTML要素のリセットや、mt-10(margin-top:10px;)などのような微調整用のセレクタも必要になる。FLOCSSでいえばFoundationやUtilityにあたる。
大概1単語で片が付くし、ネーミングは大概決まっているためここまでの検討対象に入れていなかった。
全ページ共通で使いまわすため、これらをBaseとして分類することにした。

おおむね出来上がったので

GitHubにまとめておいた。
ネーミングに使える単語数が制限されているので、モジュールは小さいものから大きいものへとやっていくのがコツではないかと思う。親モジュールに振り回されないため、再利用性は上がるはず。

RPD-CSSNaming

一応、このアイデアはHTML/CSSコーダーにとってはとっつきやすいのではないかと思う。覚える・理解することも少なめで意思統一コストは低いのではないかと。

ただ、BEMの持つ親Blockとの強力なつながりが、RPDでは見えなくなるという欠点がある。モジュールは構造のみを担い、他のBlockでも使える(そのBlock独特の装飾部分は従属セレクタが担うはずである)ので大きな問題ではないと思うが、これはどういうプロパティを従属セレクタに任せるかの判断が重要になってくる。下手に組むと使いにくくなるだろう。


RPD-CSSNamingのアイデアは、プロジェクトによっていろいろカスタマイズできると思う。小規模なサイトであれば装飾セレクタを使わずBEM的なつくりの方が効率的に組めるのではないかと思うし、関わる人が多くて大規模になるのならSMACSSやFLOCSSのルールの方に寄せていった方が管理しやすいと思う。
これでCSSのネーミングに悩む人が減ったらよいなと思う。