BEM は優れた命名規則ですが、階層が深くなるとアンダースコアの区切りが雑になり、全体的に理解しにくいコードができあがります。
今回はこのページの構造を例に BEM と違う命名規則を考えます。ページのおおまかな構造は下のようになります。
BEM を使うとアンダースコアがたくさん必要になるかもしれない
おかしなコード
BEM を額面どおりに受けとるとアンダースコアだらけになります。一部の開発者は
とするかもしれない。結局はハイフンでつないでいる。開発者は「親・子・孫」の関係を「親・子」と「子・孫」の 2 つの関係に分けて、最初の「親・子」は BEM が定めたアンダースコアのルールを適用しない。
親子の関係をアンダースコア、単語の区切りをハイフンにすると、コピー&ペーストで苦しむ。
という単語をダブルクリックしてコピーするとき、header と left__logo で分かれる。もちろんこれは「親・子・孫」の関係を壊す。そもそもアンダースコアを 2 本も使ってコードを冗長にする必要があるでしょうか。
別の書き方
ここから当サイトが採用した命名規則です。
親子関係はいつもハイフンで表す。
header-left
headerLeft-logo
headerLeft という単語はキャメルケースで、この単語そのものが親子関係(header-left)を暗示する。しかし headerLeft-logo はあくまでも headerLeft と logo の親子を意味する。
結論はこうです。自分に親がいるときは、常に「親・自分」とハイフン区切りで命名する。自分の親はいつもキャメルケースにする。キャメルケースに親の親などの先祖を暗示させる。
BEM と関係ないコーディング規約・命名規則
当サイトはシンプルな構造とデザインを徹底しています。HTML のコードはなるべく増やさず、ネストは浅くする。inline-block や flex はもちろん、box-sizing なども駆使して最小のコードで UI を組み立てる。
シンプルな HTML と css にはいくつかの規則が必要です。
- 大原則:コードをなるべく書かない
- 色やフォントサイズなどのみを指定する個別のクラスを使わない
- OOCSS を採用しない
- コピー・ペーストを悪と考えない
- クラス名に日本語を使わない
- 子要素を持たない要素に class をふらない
- float でなく flex を使う
- before や after を駆使する
- 無駄な省略形を使わない
- div で a タグを囲うことに躊躇する
- 複数形を駆使する
- 兄弟関係をアンダースコアで明示する
大原則:コードをなるべく書かない
あらゆる概念で最も複雑で扱いにくいものが入れ子です。そして html と css は入れ子を相手にします。
箱の中に箱があり、その中にまた箱があり…という構造は単純で扱いやすいように見えますが、巨大化すると高層ビルのように融通がきかないものになり、最悪はすべてを台なしにします。
入れ子を相手にするときは、階層を少なくしよう。余計なタグを書かないようにしよう。余計なクラスを定義してはいけません。
- ネストを浅くする
- タグを書かない
- クラスをなるべく書かない
これを達成するために、あなたは最初に書いたコードを一度すべて破壊する必要があります。これは過度な最適化でなく、プロジェクトが健全に進化するための手順です。
あなたが見ているこのサイトのコードを見てください。ネストが浅く、タグがほとんどなく、行数がないことに気づくでしょう。このコードは現時点で 20 回ほど破壊されています。
まずはコードを書き、徹底的に破壊し、徹底的に削る。誰が書いたコードだろうと無駄を省く。それしか道はありません。
色やフォントサイズなどのみを指定する個別のクラスを使わない
文字を緑色にするクラス green を考えてください。
一行しかないこのクラスがあちこちに散らばった html は最低のコードです。フォントを緑色にしたい要素に color: green; を指定してください。
色やサイズだけを指定するクラスは史上最悪のデザインです。驚くほど生産性が低く、関連するコードに悪影響を与えます。人類が生んできた css のコードで最も愚かなものが、色だけを指定したクラスです。
OOCSS を採用しない
OOCSS は html と css を根本的に理解していないので、決して採用してはいけません。プログラマーとしてでなく、純粋なユーザーになっていろいろなページデザインを見つめ、入れ子の複雑さがシダ植物のように繁栄しているさまを眺めてみよう。
冷静になったら OOCSS の思想をあらためて考えよう。次のポイントが見えてくるはずです。
第一に、入れ子はオブジェクト指向と合わない。第二に、構造はクラスでなく html のタグが決定する。第三に、複数のクラスを同一のタグに指定することは、入れ子の複雑さをべき乗に拡大させる。以上から第四に、拡張性、汎用性、可読性のすべてにおいて OOCSS は採用に値しない。
コピー・ペーストを悪と考えない
OOCSS を推奨する人は「コピー・ペーストの多い設計は愚かだ」と考えます。プログラマーは楽をするために苦労すると言われますが、コピー・ペーストのようなくだらない工程を減らす試みは、あるレベルを超えるとそれを帳消しにするハードワークを将来につくるのです。
ログイン、アップロード、利用規約の同意のボタンを考えてください。あなたは三つのボタンを同じようにデザインしたい。つまり同じ幅を指定したい。
この btn というクラスを三つのボタンに適用したいとします。そうすれば btn の width を変えるだけで、すべてのボタンの幅が変わる。考え方はいいですが、この発想を過剰に進めると、ログインとアップロードのボタンがそれぞれのページに最適化されて独自に進化する可能性が消えます。
ログインのボタンはログインページのデザイン変更に合わせて進化するべきですが、この人は省エネ精神を優遇するあまり、ログインとアップロードという本来異なる機能のボタンを共通にしたのです。もちろんそれは失敗の始まりです。
機能ごとにデザインするという原則はコーディングでなくビジネスが下します。多くのデザイナーはビジネスでなくデザインをデザインするため、不必要な省エネ精神を発揮します。バランスのいい結論はこうです。
ひょっとしたらアップロードのボタンは 160px が最適かもしれない。進化とは可能性を諦めないことで、可能性を維持するにはつまらない労力を尊重しないといけない。
入れ子を普遍的に支配する万能なフレームワークや概念などこの世にありません。あると考えることは無駄であり、それを研究する時間はコピー・ペーストよりもはるかに高くつく。
OOCSS のような省エネ精神より、不愉快なコピー・ペーストを尊重するほうが生産性は良くなります。「フレームワークのようなコーディング規約を採用しないと大規模なデザインは破綻する」という考えは妄想です。
クラス名に日本語を使わない
日本語をローマ字にしたクラス名は使わない。
日本語をローマ字にすると見映えが悪くなり、単語も冗長になります。
子要素を持たない要素に class をふらない(最重要)
document というクラス名の div 要素に p タグの子要素があるとき、この p タグにクラスはふらない。
わざわざ .document-p というクラスをつくらないでください。「タグに直接デザインを指定してはいけない」という主張は採用できません。記事の時刻を time タグで指定しているとき、他に time タグがなければ
とします。わざわざ .post-updated-time といったクラスはつくらない。タグをそのまま活用しない限り、HTML のコードは無駄なクラスであふれて汚くなります。
float を使わない
display: flex が登場してから float をほぼ完璧に追放できるようになりました。
before などを駆使する
before や after を駆使すると余計なタグを書かずにすみます。画像と文字のセット、または特殊なデザインが文字の隣にあるケースは before や after で処理する。
省略形を使わない
btn などの略語を使わない。「このくらいの略語はわかるだろう」という個人的な判断は、そのコードを読む他人(将来の自分もふくむ)にとって最適とはいえないからです。
btn でなく button と書いてください。.glb でなく .global と書きましょう。
div で a タグを囲うことに躊躇する
a タグは厄介なタグで、つい div や p で囲ってしまう。だいたいは意味がない。
上のようなコードをよく見ますが、ほとんどは無意味です。a タグを囲う div はたいてい不要です。
inline-block を使うと階層が一つ減ります。
複数形を使う
複数形を使うとコードが簡潔になります。次の HTML はどちらがいいか?
プログラマーは前者を書いてはいけない。小要素に item などの単語を使って親子関係を明示すると、孫やその子の要素の class 名は冗長になります。
post-item の子は postItem-header になる。しかし複数形を用いたコードでは
post-header です。複数形を使うと、孫世代からの class 名から一つの単語を除去できます。
兄弟関係をアンダースコアで明示する
次のコードは最悪です。
このコードは posts があくまでもメインであり、最新記事という見出しは補足にすぎません。これらサブのブロックを扱うとき、多くの人はメインとサブを包みこむラッパー(posts-wrapper)を定義します。
膨大なコードを分析した結果、ほとんどのラッパーはまともに機能しないことがわかっています。ラッパーは画面全体か、それに近い層にあるときにうまく機能します。小さいブロックのラッパーがうまく機能することは経験上ほとんどありません。つまり posts-wrapper は不要です。
次のコードはどうでしょうか?
まともなデザイナーにとって耐えがたいコードです。なぜ posts-header は posts の外側にあるのに、ハイフンで header を持っているのか?
私たちはハイフンを親子関係とみなすことによって、世界中のページを統一的なコードで理解します。デザインの勉強をしていない人もどういうわけかハイフンで親子を明示します。それほどハイフンとは強い記号なのに、上のコードはその常識を覆しています。
そこで私は次の原則を開発しました。
アンダースコアを使うことで posts_header が posts と兄弟関係にあるとわかります。ハイフンがないから、親子関係と違うことは明らかです。実際、その違和感のおかげで BEM はデザイナーに採用されるようになりました。
補足 container と wrapper
デザインを中央に揃えたいときなど、その部分をラッパーで包むことは多い。ラッパーのクラス名として container と wrapper がよく使われます。container は、それに直接含まれる要素(子要素)が複数あるときに使います。子要素が一つの場合は wrapper を用います。
子要素複数 ... container
子要素一つ ... wrapper
親要素が body のときを除いて、子要素を一つしか持たない下のような構造は避けてください。
親 home
子 home-content
孫 home-main
ひ孫 home-header, home-content
これは次のようにします。
親 home
子 home-wrapper
孫 home-container
ひ孫 home-header, home-content
JavaScript の getElementsByClassName で使うクラス名はアンダーバーで始める
getElementsByClassName で使うクラス名と CSS で使うクラス名は分ける。デザインで使うクラス名を getElementsByClassName で使ってはいけない。
getElementsByClassName はほとんどの場合なにかのループ処理で使われる。クラス名をアンダーバーで始めて、それが特殊な用途であることを明らかにしないといけない。