昨日も少し書いたEvery Declaration Just Onceアプローチ(以下EDJOと略す)について、皆が目を瞑っているOOCSSの欠点、CSSが持つ特徴、HTMLとの兼ね合いという点からもう少し書いてみたい。これについては未だ誰ともちゃんと議論していない。機会があったらこの記事をベースにでも誰かと話してみたい。

上記Googleの文書は、主にパフォーマンスの観点で書かれている。どうしても長くなりがちな定義を分散して書くよりも、能動的に短くすることができるセレクターを分散して書いた方が、プロダクションにおいてリリースされるCSSファイルのサイズを小さくすることが可能だろうというものだ。同時にこの文書の筆者は自身のブログで、より自然にCSSを書くための手法(原文: 「The Natural Way of Writing CSS」)としてこのEDJOという手法について述べている。

僕は主にそういったCSSの書き方という観点からEDJOに注目した。この記事もそれに従って書かれている上、パフォーマンスについてはGoogleの文書で十分に書かれているので、以下では特に触れない。

OOCSSとは

まずはOOCSSについてざっと触れておく。OOCSSではいくつかの定義(プロパティーとその値のセット)を組み合わせることにより再現できる見た目をオブジェクトとみなし、それを継承・拡張していく。オブジェクトは一定の意味を持つ単位で作成され、それを組み合わせることで振る舞いベースで、ウェブサイトのビジュアル・デザインを完成させていくことができるというわけだ。

実際に継承と拡張を行う手段は、オブジェクトとして定義されたクラスを複数組み合わせていく手法、いわゆるマルチ・クラスによる。今ではCSSプリプロセッサーなどもあるが、例えばSassの@extendを使ったロジカルな継承は開発者間での濃いコミュニケーションによる意識の摺り合わせが必須とも言えるので、まだまだマルチ・クラスで行われている、行われるだろうと言って良い。

もし仮に後で書かれたセレクターの方が必ず強いというようなシンプルな仕組みであったら、マルチ・クラスはその想定通りうまく機能しただろう。しかしそうはいかない。

セレクターの詳細度

CSSではセレクターの詳細度というものがある。例えば.fooより.bar .fooが強いというような、セレクターの書かれた順序を越えて定義が適用される仕組みだ。

body h1 {
  font-family: serif;
}

h1 {
  font-family: sans-serif;
}

このCSSの場合h1serifで表示されることだろう。OOCSSにおいてマルチ・クラスを利用する場合、このセレクターの詳細度というものと格闘する必要が出てくる。なぜかと言えばオブジェクトとして定義されたルールセットの順序通りにビジュアル・デザインが適用されていくとは限らないからだ。

詳細度の仕組みは仕様を読むと簡単に書かれているが、実際にはそこそこ複雑だ。自然、その理解度が人によりまちまちであることも頭に入れておく必要がある(僕も正直8割強くらいしか理解している自信はない)。そうなるといかにしてこのような仕組みが発動しないようにCSSを書くか、ということになる。

クラスの命名規則

つまりセレクターでクラス名を二つ以上使わないようにし、すべてのセレクターの詳細度を等しくするわけだ。もちろん要素名やIDなどはこの世に存在しないものとみなして忘れ去ってしまえば良い。そうすることですべてのオブジェクト(=ルールセットに書かれた定義たち)はひとつのクラス名で表現されたセレクターにより割り当てられていくため、その書かれた順番に従ってその定義が上書きされていくようになる。

問題はクラス名の付け方だ。詳細度の仕組みの都合上、クラス名を複数組み合わせることはできない。つまり以下のようなセレクターは書くことが出来ない。

.global-header .nav .item .icon {
  ...
}

従ってこのようなクラス名を付けることになる。

.global-header-nav-item-icon {
  ...
}

HTMLは当然のようにこうなる。

<header class="global-header">
  ...
  <nav class="global-header-nav">
    <li class="global-header-nav-item">
      <span class="global-header-nav-item-icon"></span>
      <span class="global-header-nav-item-title">Blog</span>
    </li>
    ...
  </nav>
</header>

おおっと。

マルチ・クラスによる割り当てになるので、実際にはもう少しシンプルな値にはなる。が、それと同時に短いとはいえ別のクラス名が追加されていくので、class属性の値は概ねこのような複雑なものになっていく。更にこういった単純に連結したり組み合わせたりするだけの命名規則では表現できない概念を補足するように鍛えていくと、SMACSSMindBEMding、そして今最も注目度が高いSUIT CSSの命名規則へと進化することになる。

かくしてHTMLでは各要素にclass属性として20から50文字くらいの値がちょくちょく割り当てられていくようになった。CMSやビルド・ツールを前提にしないと保守できない状態だといえるだろう。

Every Declaration Just Once

すべての定義を一度だけしか書かないこのアプローチ、EDJOではOOCSSとはCSSを書く手順が逆になる。

OOCSSではオブジェクトといういくつかの定義の組み合わせであるルールセットをまず書き、それにクラス名を使って名付ける。そしてHTMLに戻り、そのオブジェクトを割り当てたい要素に対し、クラス名を振っていく。

EDJOではまずHTMLで適切なクラス名(IDでも良いだろう)を振っておく。誤解を恐れずに言うのならば、セマンティックなクラス名とかそのようなもので、ウェブページにおいてその要素がどのようなものかを表現するクラス名だ。IDでも良いだろうし、HTML5の強い意味付けを持つ要素をきちんと使い分けているならクラス名やIDを付けなくても良いこともあるかもしれない。またクラス名は再利用しないので、抽象化した名称(例えば.clearfixであったり.column-wrapperなど)である必要もない。

そしてCSSではビジュアル・デザインを決定する定義をまず書き、それを適用するセレクターを割り当て、ひとつの定義ごとにルールセットを作っていく。例えばこのような形だ。わかりやすいように様々なパターンでセレクターを書いてみたが、実際には開発者やプロジェクトごとにパターン化されてくるだろう。とりあえず一意に確定する形で書いておけば、後で置換をかけることでそのフォーマットを統一することも容易いはずだ。

body > header,
main,
.searchbox,
[role="contentinfo"] {
  margin: 0 auto;
}

body > header,
[role="contentinfo"] {
  background-color: #333;
}

main {
  background-color: #fff;
}

.searchbox {
  background-color: #999;
}

EDJOではセレクターの詳細度はほとんど無視することができる。なぜならある要素を表現するセレクターは常に同じで良いからだ。つまりある要素に複数のクラス名が割り当てられることはないし、同じ要素を違うセレクターで参照することもないということで、それゆえに値の上書きがそもそも起こらないということになる。状態を表すようなクラスは別途追加する必要が出てくるが、例外はそれくらいだろう。

このようにEDJOでは、セレクターの詳細度というあまり理解されていない仕組みとクラスの命名規則という守らせることはおろかまず作ることからして難しい仕組みを共に意識せずにCSSとHTMLを書いていくことができるということだ。それはつまりHTMLはHTMLで書くことができ、CSSはCSSで書くことができ、更にその両者の結びつけを同じ定義を二度書かないという単純明快なルールのみで行うことができるということでもある。


OOCSSの欠点はその思想や哲学、ベースとなった技術の問題ではない。ツリー構造を持つHTMLやカスケーディングという独自の継承の仕組みを持つCSSとは根本的に相性が悪く、命名規則というような運用におけるルール付けでカバーしなくてはならないところだ。もしHTMLやCSSがもう少し違うものだったら、OOCSSは間違いなく論理的で明確な方向性をウェブデザインに与える唯一無二の手法であった、そしてありつづけるものだったろう。しかし現実は違うし、未来もそうなりそうもない(セレクターのネストが可能になるとわからないけれども)。

そこでCSSにおいてその力不足を補い、OOCSSのような論理的なアプローチを可能にしようとするために生まれたのがCSSプリプロセッサーだった。しかしこれもOOCSSの欠点を完全にはカバーすることは出来てはいない。そのため既に名前だけは何回か出したSUIT CSSの命名規則のようなものが、ここに至っても生まれた(生まれてしまった)というわけだ。

EDJOがCSSを本来あるような形に戻したものだと捉えると、もはやCSSプリプロセッサーはCSSを無闇に複雑にしてしまっただけにも感じられてしまう。EDJOならCSS Variableのみのプリプロセッサーで十分機能しそうだ。


ひとつ、声を大きくして謝りたいのは、フロントエンド側じゃない人たちのCSSの書き方にダメ出しをしていたことだ。過去の僕はOOCSSをいかにして再現するかを重要視しており、OOCSSへの理解に欠ける人たちによるEDJO的な書き方(定義があるところに雑にセレクターを追加していく書き方)に対してかなり厳しく反論していたように思う。OOCSSを採用している以上、そういったEDJO的な書き方を否定することは間違っていたわけではないのだけど、その時の論理は「OOCSSだから~」というような、今思うと曖昧な、当時も理由としては弱いと薄々気付いていた言葉で押し通していた。

その頃にもう少しOOCSSの欠点について掘り下げていればそういった書き方にも一理以上の何かがあったことに気づけたはずだ。広く受け入れられているアプローチだからというような理由で盲信するのは良くないが、それを実践するのはなかなか難しい。もちろんこのEDJOというアプローチにも言えることだ。気をつけながらEDJOの世界をもうちょっと掘り起こしていこうと思う。

もしMindBEMdingのようなBEMライクな命名規則から、よりわかりやすく強力なSUIT CSSの命名規則に移行しようかなと考えている人がいたら、ちょっとだけEvery Declaration Just Onceというアプローチを試してみて欲しい。僕はこのアプローチを試した結果、OOCSSの実装の難しさにより嫌いになりかけたCSS(Sassでかろうじて理性を保ってやり過ごしてきていたが)を本気でもう一度好きになれそうな気がしてきている。HTMLとCSSが疎結合する未来を夢見ていた人達なら、OOCSSが唯一無二の解ではないという前提だと、このアプローチに未来を見出すことができるのではないだろうか。

追記

参照したGoogleのEDJOに関する文書とこの記事の内容が乖離していたことに気づいていなかったので、本来的に参照するべきJens Oliver Meiertのブログ記事へのリンクを追加した。両文書は同じ筆者による記事ではあるが、違う観点によって書かれているので、両方参照するべきだった。