カーゴ・カルトCSS
CSSを書いたり管理したりするにはなんらかの方法論があった方が良い、と広く考えられている。しかし実際に取り入れられている手法の中には、セマンティクス上の品質や、長期にわたるメンテナンス性に悪影響を与えるものもある。ここでは、CSSの「フレームワーク方法論」として提唱されているテクニックの問題点や、その問題を僕たちウェブ・ディベロッパーがどうすれば解決できるかについて論じてみようと思う。
現在、CSS開発におけるフレームワーク方法論として、BEMなど類似のテクニックがいくつかあるが、もっとも有名なのはOOCSSだろう。これらの方法論はCSSにオブジェクト指向プログラミングの原則を適用しようと試みる。しかしながら、両者の間にはそもそも宣言型スタイル言語とオブジェクト指向ソフトウェア設計原則というコンセプト上の不一致がある。その結果、経験の浅いディベロッパーが気づきにくいような複雑な問題を持ち込んでしまう。たいへん困ったことに、著名なブロガーたちが「ベスト・プラクティス」として喧伝しているおかげで、これらの方法論の採用が広く見られるようになってしまった。ごく一部の高いトラフィックを持つサイトを除いて、これら方法論によって得られるとされる利点を証明する根拠はない。このことからもわかるとおり、CSSのフレームワーク方法論は、人を惑わせ害を及ぼすカーゴ・カルトの典型であると僕は考える。
セマンティクス
ウェブは基本的にセマンティックなメディアだ。様々な言語、文化、性、世代、肉体的および認知的能力を持った人々が、あらゆる種類の技術を通じて利用できることを目指すプラットフォームとして、この点はきわめて重要だ。ウェブがどうあるべきか、またはどう見えるべきかを示す唯一のビジョンはない。明らかに限られた領域で開発しているのでない限り、セマンティックなアプローチこそがウェブ・ディベロッパーのあらゆる行動の中心であるべきだ。
HTMLを書くとき、ウェブサイトやアプリケーションのインターフェースやコンテンツのセマンティクスを表現するにはおもに3つの方法がある。コンテンツをマークアップする要素タイプ、ほかとは異なる個別の要素を特定するID、そして要素がどの集合に属すかカテゴライズするクラス名だ。
この3つの中で、HTMLドキュメントにプレゼンテーションを結びつけるにはクラス名がもっとも一般的に使われている。クラス名について論じるにあたって、W3Cの最新のHTML5勧告候補が次のように記している点に注意してほしい。
class
属性の仕様のどこを見ても、この属性の果たす目的として、コンテンツのセマンティクスを表現するということ以外は書いていない。にもかかわらず、じつに多くのウェブ・ディベロッパーがclass
属性について「CSSクラス」などといった間違った呼び方をしている。タンテク・チェリクはこう書いている。
フレームワーク方法論は勧告と完全に食い違っているが、彼らは勧告を軽視する道を選んだ。その手のプロジェクトでのクラス名はプレゼンテーションを表現している必要があり、非セマンティックにならざるを得ない。そうではないと主張しているにもかかわらず。それにしてもなぜ、セマンティックなクラス名がそこまで重要なのか? もっとも基本的なレベルで言うと、ティム・バーナーズ゠リーが「ウェブの普遍性」と表現したところの、プラットフォームに依存しない、包括的デザイン原則の維持が目的だ。また、いま僕たちが作っているものが、将来どんな技術によって利用されることになるかは予測できないからでもある。
この件についての例としてMicroformatsが挙げられる。Microformatsが生まれたのは、ドキュメントの製作者が住所やカレンダーといったよくある仕組みを表現するのにセマンティックなクラス名を使ってきたからにほかならない。このようにデータを表現する上での慣例を体系化できれば、マークアップはウェブサイトの利用者だけではなく、それをまったく新しいやり方で利用するソフトウェアにとっても有益なものになる。Microformatsがセマンティックなクラス名は有用だと証明しているのに、それが否定されるのを見ると残念な気持ちになる。
これは恣意的な表現だ。今日作られ、発表されているコンテンツの、将来にわたる利用のすべての可能性に対して、「合意を得た名前の集まり」とは。この表現はCSSフレームワークのイデオロギーを正当化したいがためのものだ。非セマンティックなクラス名を許すと、ドキュメントの構造とコンテンツがプレゼンテーションの影響を受けるようになってしまう。それは明らかに関心の分離に反する。
メンテナンス性
ウェブサイトを作るとき、僕たちはつねに、そのサイトがいつまでも簡単にメンテナンスや変更ができるよう、充分に将来を見すえて考えなければならない。今日のウェブサイトやアプリケーションは、ますます大きなフロントエンド開発チームを必要とするようになってきている。そういった環境では、CSSを書くための方法論がチーム全体にうまく取り入れられることがとても重要だ。チームの何人かはプロダクトの一生のうち一時期しか関わらないだろうし、何人かはプロダクトの初期には参加していなかっただろう。そこではまた、CSSの実装に関して多くの難題が持ち上がる。たとえば……
- コードの繰り返し・重複
- プロダクトを通じた一貫性
- パフォーマンス
CSSフレームワーク方法論の背後にあるもっとも重要な目的は、このうち最初の、サイト中のCSSコードの重複の問題を解決することだろう。クラス名による短いセレクターは、実用的で、あらゆる要素間でルールを共有できる。次の例を見てほしい。このクラスはどんな要素にも同じボーダー、パディング、タイポグラフィを適用する。
.box-standard {
color: blue;
border: 2px solid blue;
border-radius: 5px;
padding: 20px;
font-family: Helvetica, Arial, sans-serif;
font-weight: normal;
font-size: 1rem;
line-height: 1.4;
}
このクラスをどのような要素に適用してもそのスタイルになる。もしそのスタイルの全部は必要なく、かわりに特定のプロパティを上書きしたいなら、次のソース例のように別のクラスを宣言すればいい。これらのクラスの特定度は同じなので、後者の宣言が優先される。
.box-special {
color: red;
border-color: red;
font-weight: bold;
}
<div class="box-standard box-special">
Here is a special box!
</div>
やりたかったのはコードの重複を減らすことだったはずだが、この方法でクラスを実装すると、マークアップはこの両方のクラスの存在に永遠に縛られることになる。どちらのクラスも特定のセマンティクスを伝えていないにもかかわらず。
こんなときこそ求められるのが、LESSやSassなどのCSSプリプロセッサーにあるミックスインや継承の組み合わせだ。必要なプロパティはミックスインやプレースホルダーでひとまとめにできるし、もろにプレゼンテーショナルな名前をつけても(デバッグのためにあえてそうしない限り)生成されるコードには現れないし、そのルールはどんなセレクターにも使い回せる。以下はSCSS構文でのミックスインの簡単な例。
@mixin news-item($color) {
border: 2px solid $color;
border-radius: 5px;
padding: 20px;
font-family: Helvetica, Arial, sans-serif;
font-weight: normal;
font-size: 1rem;
line-height: 1.4;
}
div.news {
@include news-item(blue);
}
div.breaking {
@include news-item(red);
font-weight: bold;
}
<div class="news">
Here is a news item.
</div>
<div class="breaking">
Here is a breaking news item!
</div>
ミックスインのスタイル宣言に変更を加えるとつねにdiv.breaking
セレクターも連動する。マークアップとはこれ以上ないくらい疎結合の状態だ。こういったミックスインをネイティブなCSSで定義できないのはとても残念だが(ありがたいことにようやく正式に検討されはじめた)、プロとしてCSSを書くならCSSプリプロセッサーを使わない理由はどこにもない。
疎結合や関心の分離といったものは、プロジェクトの将来にわたるメンテナンスにおいて極めて重要だ。マークアップとCSSを密結合にしすぎると修正のコストが増大してしまう。CSSがまだ生まれて間もない頃には、DreamweaverやFrontpageといったWYSIWYGエディターの増殖が多くのウェブサイトを次のようなコードへと導いたものだった。
<span style="display: block; font-family: Arial, sans-serif; font-size: 11px; color: blue; border-style: solid; border-color: blue; border-width: 2px; padding: 20px;">Here’s a box.</span>
<span style="display: block; font-family: Arial, sans-serif; font-size: 11px; color: blue; border-style: solid; border-color: blue; border-width: 2px; padding: 20px;">Here’s another box.</span>
<span style="display: block; font-family: Arial, sans-serif; font-size: 11px; color: blue; border-style: solid; border-color: blue; border-width: 2px; padding: 20px;">Here’s yet another box. Noticing a trend yet…?</span>
こういったインラインのスタイルに基づいたマークアップは、90年代後半にウェブをおとしめていたグチャグチャのタグの山に比べれば多少はまし、というものでしかない。デザインが変更されるたび、これらすべてのインライン・スタイルを追跡して書き換える必要がある。僕らはこの手の地獄のような開発からは逃げ出したが、しかし悲しいことに、これまでの教訓も忘れ去られてしまったらしく、いまだに次のようなコードに遭遇することがある。
<span class="display-block blue-box font-arial color-blue solid-blue-border padding-20">Party like it’s 1999!</span>
<span class="display-block blue-box font-arial color-blue solid-blue-border padding-20">Hey, have you checked K10K.net lately?</span>
<span class="display-block blue-box font-arial color-blue solid-blue-border padding-20">Wassuuuuuuup!</span>
極端な例に見えるかもしれないが、これはCSSフレームワーク方法論が「モジュラー化」を追い求めてセマンティックなクラス名とセレクターを捨てた当然の帰結だ。CSS内でのコードの重複を避ける一方、その重複はただ単にマークアップに転移し、その過程で密結合という副作用を招いている。
またCSSフレームワーク方法論の別の目標として、高いパフォーマンスの実現というものもある。パフォーマンスは多くの要因に依存するものだ。ウェブページを利用しているクライアントの種類や、接続速度、コンテンツのキャッシュなど。基本的に、CSSのパフォーマンスに明らかに影響するのは次の3つだ。
- HTTPリクエストの数
- キャッシュの状態
- ドキュメントのサイズ
このうち最初の2つはCSSフレームワーク方法論の範囲ではないが、3つめにはクラス名の再利用というかたちで取り組んでいる。その意図は、スタイルのプロパティの宣言を可能な限り少なくすれば、CSSはおのずと小さくなるだろう、というものだ。
しかしながら、そういったCSSの配信は誤った方向に向かう恐れがある。セレクターを短く、ルールを少なくし、多くの束縛を(たくさんの長いクラス名とともに)マークアップに持ち込んでも、転送されるデータ量は必ずしも小さくならない。この方法は多くのものをCSSファイルからHTMLドキュメントに運び出すが、CSSはたいていCDNによる積極的なキャッシュと配信がおこなわれるのに対し、HTMLはまったくキャッシュされないかもしれないのだ。
CSSセレクターを短く保つことはパフォーマンス上の利点があると、CSSフレームワーク方法論の支持者はしばしば引き合いに出す。しかしこのことはすでに反証されており、モダンなブラウザー・エンジンはごくまれな場合を除いて充分に手際が良く、セレクターを書き換えることによるスピードの向上は取るに足りない程度のものだ。一方で、フロントエンドのパフォーマンスには追求する価値があるのは間違いない。
CSSフレームワーク方法論のもっとも深刻な問題のいくつかは、古いコードを見返したときや、新しいチーム・メンバーがプロジェクトに加わったときに露わになる。これはおもに「探しやすさ」をほとんど考慮していないことが原因だ。ニコラス・ギャラガーはフロントエンド・アーキテクチャーについての記事で「クラス名はディベロッパーに有益な情報を伝えるものであるべきだ」と言っているが、僕はこれをもう一歩推し進めたい。セレクターはディベロッパーに有益な情報を伝えるものであるべきだ。両者の目的は同じで、そのルールがどこで使われているのか知りたいというものだが、セレクターはコンテキストを伝えるという点でずっと有益だ。もしセレクターが決められた範囲内の要素にだけ適用されるということがわかれば、そのルールの背後にある意図を理解するのはずっと簡単だ。次に挙げる、名簿のリンクをスタイリングする2つの方法について考えてみてほしい。
.list-link {
font-weight: bold;
text-decoration: none;
}
またはこう。
ul.members li a {
font-weight: bold;
text-decoration: none;
}
後者は、そのコードベースにはじめて触れたり、しばらく離れていたりして馴染みが薄かったとしても、難なく理解できるはずだ。そしてまた「探しやすい」という性質も持っている。新入りのデベロッパーの場合を考えてみよう。彼らからすると、そのCSSコードベースを丸ごと読んだり(あるいは覚えたり)しない限り、そこに一貫したルールがあるかどうか知りようがない。彼らに過度な期待をするのは現実的ではないだろう。彼らは十中八九、繰り返しを減らすという目標に完全に反して、同じ結果を得るために別の新しいルールを書こうとする。
探しやすさの問題と関連して、フレームワーク方法論はまた、ソフトウェアが肥大する要因にもなる。具体的なセレクターを書けば、そのセレクターがすでに不必要になっていないかどうかを判断する材料になる。先ほどの後者の例では、名簿のリストがウェブサイトから削除された場合、そのCSSブロックをまるごと消すことができる。一方でフレキシブルなクラス名ベースのセレクターはほかで使われている可能性があり、そのルールを削除しても安全かどうかすぐにはわからないので、クリーンアップに地道な努力を必要とする。
パフォーマンスについて最後に、マット・ウィルコックスの完璧な説明を引用しよう。
で、僕らに何ができる?
フロントエンド・ディベロッパーたちは何年もの間、CSSフレームワーク方法論なしで、大きなウェブ・プロジェクトをおとなしく、そして上手いこと進めてきた。OOCSSやBEMといったものを使わずともこういったプロジェクトがなんとかなったという事実は、ほかに効果的な方法があるという証拠だ。ここでは、僕と一緒にCSSについて議論してきた人たちからの助言を、僕が経験から得たいくつかの提案とともにいくつか紹介したい。
「IDを使ってくれ、頼むから」
CSSフレームワーク方法論者の中には、IDを使うのは良くないと言う人たちがいる。IDは特定度(specificity)が高いので、(いまいましい)!important
なしではルールを上書きできなくなってしまう恐れがある、というのがその理由だ。僕はこの意見に強く反対する。アンガス・クロールはこう書いている――「厄介ごとを避けようという方針は困ったものだ。なぜなら、言語をマスターするにはその言語のすべてを知らなければならないし、恐れたり逃げたりすることは知識を得ることの妨げになるのだから」。もし君がプロのウェブ・ディベロッパーなら、特定度がどのような仕組みで、そしてそれが君の書くセレクターにどのように影響するのかを理解すべきだ。さらに、IDはドキュメントの構造上妥当だし、とても有益な機能とセマンティックな目的がある。
「セレクターは必要に応じて具体的かつ説明的に。それ以上でも以下でもなく」
もしそのCSSが色んなところに出現する可能性のある要素をターゲットにしているなら、そのようなセレクターを使おう。もしそのCSSがごく限られた場合にしか使われないなら、そのようなセレクターを使おう。なにより、誰かが半年後に見たときにどんな目的なのかわからないような、いい加減なセレクターにしちゃいけない。
「再利用可能なモジュールなどない(実際に再利用されるまでは)。リファクターやリライトはあとまわし」
特定度の高いCSSを書くことに不安を覚えたとしたら、それはつまり時期尚早な最適化をしようとしているということだ。まず役目を果たすのに充分なCSSを書き、そしてそれが再利用されることが明らかになったときにはじめて適切なミックスインとしてリファクタリングしよう。来週には優先事項が変わり、その機能をまるごと捨てることになるかもしれないって? でも、半年後に誰かが見たときに目的が理解できず、消しても大丈夫かわからないような、余分なクラス名のセレクターの山なんて誰もほしくないはずだ。
「たくさんのファイルもひとつのビルド・プロセスで」
君はプロのウェブ・ディベロッパーだ。コードを書くのにもうメモ帳を使ってはいないだろうし、作業にもっとも適したツールを使うこと。CSSプリプロセッサーのツールにはフレームワークやプラットフォームにかかわらず利用可能なものがある。そしてまた、プロジェクトを越えてCSSを効果的に再利用するにはどうすればいいのかという良い事例もたくさんある。ほかのプロジェクトではどのように仕事をしているのかに触れ、知見を広げよう。
「静的なプロトタイプを作ろう。すべてのスクリーンショットを残そう」
ウェブサイトやアプリケーションはふつう、よくあるイディオムの集まりから出来ている。ラベルと入力ボックスからなるフォーム、nav
要素内のリンクのリスト、ラベルと値のリストなど。どのような機能の実装にとりかかるにせよ、まずは静的なHTMLとして適切な構造のマークアップを組み立てることからはじめよう。この方法はコードがあとあと破綻しないためのテスト・ケースになるだけではなく、チームの新しいメンバーが途中経過を知るための助けにもなる。また手動でも自動のテスト・スイートでも、ページやアプリケーションのスクリーンショットを撮っておけば、継続的インテグレーションのテストにおいて壊れている箇所を見つけるのに役立つ。
複雑なウェブ・アプリケーションにおいて、優れたCSSアーキテクチャーを構築するのは難しい(結局のところ、良いフロントエンド・ディベロッパーがこれほど求められる理由はここにある)。かと言って独善的な「ルール」を課してしまうと、それはプロジェクトに新しく加わる人にとってもすでに関わっている人にとっても、事態をより困難にするだけだ。そのルールは最後には、無駄な複雑さや、うんざりするようなメンテナンス・コストを生じることになる。
自分が作っているプロダクトの目的を自問してみれば、プロダクトのあらゆる面について、どう表現するかだけではなく、どう構造化するかについてより重視するようになるはずだ。ウェブはセマンティックなメディアである、ということを指針にしよう。
謝辞
この記事を書くにあたっては、以下の人たちからのフィードバックに大いに助けられた。Mark Norman Francis、Brad Wright、Ross Bruniges、Jake Archibald、そしてPatrick Griffiths。
脚注
- Two Hard Things ↩
- W3C: Semantics, structure, and APIs of HTML documents ↩
- Why you should say HTML classes, CSS class selectors, or CSS pseudo-classes, but not CSS classes ↩
- Twitter / alex_gaynor: Quick test for if your HTML … ↩
- About HTML semantics and front-end architecture ↩
- The Cost of Performance ↩
- The Art of Unix Programming ↩
- CSS Lint is harmful ↩
- Wikipedia: Form follows function ↩