Web制作者のためのCSS設計の教科書ーモダンWeb開発に欠かせない修正しやすいCSSの設計手法

壊れない完ぺきな設計を求めるのではなく、壊れた時に勇気をもって修復できる設計を!

チャプター1

運用期間が長く、規模が大きくなっていくWebサイト、アプリケーションのコードを管理するのは大変難しいものです。正確に言えば、中長期にわたって運用していく中で、コードを無駄に肥大化させず、思いもよらないバグを招かないようなコードを書くというのが容易ではない。

フロントエンドの仕事は、デザイナーがフォトショップで作り上げたデザインガンプをブラウザ上で表現するだけで完結することはない。

良い設計のゴールーGoogleのエンジニアでありHTML・CSS関連のツールやドキュメントを残しているフィリップ・ウォルトン氏

・予測しやすい

・再利用しやすい

・保守しやすい

・拡張しやすい

 

1-2.破綻しやすいCSS

単純に書くだけであれば簡単なCSSですが、運用をしていくとなると扱いが非常に難しくなります。簡単ではあるがゆえに、自由にかけて、その場限りではそれらしい見た目をつくる、というのが裏目に出てしまい、コードを破綻させます。

基本的には、.selector {property: value }といったフォーマットと、スタイルを作るのに必要なだけのプロパティとその値を覚えればいいだけです。

効率的ではないCSS設計の例

①HTMLの構造に依存している

多くの場合、サイトやアプリケーションの機能仕様やデザインが変われば、HTMLのマークアップも変わるため、HTMLの構造に依存いているJavascriptやCSSも修正する必要があります。

ここで簡単なHTMLの変更と、それに伴うCSSの修正が必要なケースを紹介します。

 

これがもし次のようなマークアップになった場合、CSSも変更する必要があります。

 

あらかじめ次のようなclassを使ったマークアップにしていればどうでしょうか。

 

あわせてCSSは次のようにします。

 

かりに先ほどのようなマークアップの変更があったとしても、同じclassを与えればCSS側は基本的に変更する必要がなくなります。

 

この例に限らず、HTMLマークアップへの依存がもっと深いセレクタを書いてしまう場合があります。

 

デザインガンプを見ながら、classをほとんど使わないシンプルなマークアップで作ったり、CSSの:first-childのような賢いセレクタを使ったりすること自体は悪いことではありません。

しかし、中長期にわたって運用されるプロジェクトにおいては、これらが修正範囲を広げてしまう要因になることが多いのです。マークアップがシンプルであることは大事ですが、CSS設計とのバランスを考える必要があります。

②スタイルを取り消している

スタイルの取り消しというのは、一度定義したルールを何かしらの理由で0やnoneといった値でリセットするようなことを指します。

例えば、前述の例と同じ、下線つきの見出しのデザインがあるとします。

 

基本的には、このスタイル定義で通用するものの、あるタイミングで下線が不要なデザインの見出しが必要となり始めたとします。その場合、おそらく多くの人はつぎのようなルールを新たに追加するでしょう。

 

このパターンがまさに下線を取り消すためのルールが追加された状態です。それに対して、次のパターンでは下線を追加するためのルールを作ります。

 

両者を見比べれば、単純にコード量が減っていることが分かります。これは単純な例ですが、より複雑なデザインやコードにおいては、無駄なコードが増えてしまう要因になりえます。ルールを書き進めて行くときには、定義したルールを取り消すのではなく、追加していくようにするとよいでしょう。

もしborder:noneやpadding:0のような取り消しのルールが増え始めた時、または1つのルールセットに、多くのプロパティが宣言されているのが目立つようになったときには、立ち止まってコードを振り返ってみることをおすすめします。

③絶対値を多用している

デザインガンプの見た目を再現することにフォーカスすると、要素ごとの余白や座標、フォントサイズの値をPhotoshopなどのツールでピックアップし、それをCSSへと置き換えるのに務めるでしょう。

基本的に、Photoshopなどのツールからこれらの値をピックアップする場合、それらは16px、24pxというような絶対値になります。ここではフォントサイズとその行間における絶対値指定のバッドプラクティスを紹介します。

 

基本となるフォントサイズに対し、リード文にあたる段階だけフォントサイズが大きくなるようなデザインがあるとしましょう。素直にこれらのフォントサイズと行間(行送り)の値を取得してCSSに反映させるなら次のようになります。

 

しかしよく値を見てみれば、どちらのline-heightも、フォントサイズに対する倍率は1.5です。つまりは次のように書き換えることができます。

 

 

このリファクタリングのポイントは、次の3つです。

・line-hightを相対値にすることで、フォントサイズが仮に変わっても、同じ倍率を保つ

・line-hightは値を子要素に継承するため、子要素に同じline-hightを指定する必要がない

・line-hightプロパティの単位にない値にすることで、その要素のフォントサイズに対する宇倍率で行送りが計算される

1つ目は、基本的に1.5倍であることを前提にすれば、フォントサイズの調整が入ったとしても、line-hightの値を修正する必要はありません。

2つ目についてはCSSの継承についての理解が少し必要です。フォント関連のプロパティ(color, font-size, line-hightなど)や一部のプロパティは、値を子要素に継承します。例えばbody{…}にcolor:redとすれば、colorプロパティで任意のしていをされていない要素の文字色はすべて赤色になるはずです。

3つ目は、line-hightプロパティのベストプラクティスです。意図的にあえて絶対値や単位を必要としない限り、単位を付けない方が好ましいです。絶対値および絶対単位ではなく、emや%といった相対単位もありますが、それでは今回の例では意図通りの行送りにはならないので気を付けるようにしましょう。

 

④修正しやすいCSSへ

ここまで挙げた例は非常に小さなケースであり、本当にこの程度のことであれば、CSSを書き直すことも大したコストではないかもしれません。しかし、時間をかけてコードはどんどん複雑になっていくものです。そうすると、ちょっとした修正のはずなのに、多くの時間を割かなければいけなかったり、その小さな修正がどこかで大きなバグを生み出したりする可能性があります。

こうした破綻を起こさないためのCSS設計を考える、というのはいいことではありますが、現実的に破綻を起こさないのは不可能といえるでしょう。

本書で解説するCSSの設計とは、そしてその重要さというのは、いかにして破綻しないCSSを書くのか、と言うことではなく、修正しやすいCSSを書くという事につきます。それはつまり、何か問題が発生したときに、勇気を持ってコードを手に入れることができるかということです。

それを踏まえて考えることがCSS設計である、といえるでしょう。

 

⑤モダンなサイト構築ワークフロー

この数年でWebサイト、Webアプリケーションの構築ワークフローは変わりつつあり、従来のうぉーたーふぉーる型の直線的なワークフローではなく、短いサイクルで検証を繰り返すワークフローを取り入れるプロジェクトが増えています。

もちろんそのプロジェクトの性質などによって最適なワークフローは変わります。例えば、マルチデバイス対応としてレスポンシブWebデザインを取り入れるようなプロジェクトではどうでしょうか。

時間をかけて多数のスクリーンサイズ分のデザインガンプを一気に作り、それを完全にブラウザで再現して動くようにするとしましょう。

しかし動くものを実際に見た時点で、デザイン、機能その他がいまいちなので、デザインガンプの修正からやり直す、という経験がある人は少なくないでしょう。

直線的なワークフローでは問題が発生したときの手戻りの幅が大きく、それだけコストもかかりますし、スケジュールが決まっていれば、それに間に合わせるために品質を下げてしまうようなこともあるかもしれません。

そのようなワークフローに対し、もっと小さな機能単位で設計・実装・検証・改修を行うワークフローが近年求められています。

フロントエンドができることの1つは、早期にHTMLのプロトタイプを作成する事です。PNGやJPEGといった画像を印刷したり、ブラウザで疑似的に見せたりするのではなく、実際にブラウザでどのように振舞うのかを検証することができます。

HTMLとして動くものを見て初めて、ブレークポイントごとのUIやデザイン要素の変化、機能面でどういった対応が必要かを考えることができます。

そうすると、小さな単位での検証と改修が発生し、必要に応じてフロントエンドの領域で改修が発生します。

そこでCSSもある程度の変更に耐えうる、または変更があっても容易に改修ができるような設計であることが求められ、開発のスピードを早くするために必要となります。

変更の範囲というのは、さまざまですが、例えば、右カラムになったものを左カラムへ、またAというページにあったXという要素を、BというページのYという要素と入れ替えるといったことが、容易に行えるようになると、プロトタイプとしての価値が上がります。

このような入れ替えが利くようにするための基本設計として、要素を部品=モジュール、コンポーネントと考える設計が求められる。

 

チャプター2

CSSの基本を振り返る

CSSの設計を考えるためには、CSSの基本であるセレクタと詳細度のことを理解しておく必要があります。CSSが破綻していく要因の1つは、セレクタが複雑になってしまい、管理しきれなくなってしまうことです。第三章のコンポーネント設計を考える上でも重要なポイントとなるため、ここで復習しておきましょう。

2-1.CSSセレクタと詳細度

CSS設計の話に深く入る前に、CSSのカスケ―ディング、セレクタの仕様を振り返ってみましょう。

※カスケーティングの基本

CSSはカスケーティング・スタイルシート(Cascading Style Sheet)の略です。カスケード(滝)のように、先に宣言されたルールセットが、その条件などに応じて、後から宣言されるルールセットによって、継承または上書きされます。

 

例えば、次のようなルールセットがあった場合、後に記述されている.saveのルールの方が適用されます。CSSでの記述上、もし.buttonnと.saveを入れ替えた場合は、やはり.buttonのほうが適用されます。

 

ただしこれらは同じ詳細度である、という条件が満たされている前提です。

 

※詳細度

CSSのセレクタは、どの要素にスタイルを適用させるのかを指定します。次のコードは、successという値をclass属性に持つ要素の文字色を、greenにするルールセットです。

 

セレクタには、詳細度(Specificity)という条件があります。セレクタの内容がより具体的・詳細であるほど、そのルールセットが優先されます。詳細度は次の順番で高くなります。

詳細度高

1.!Important

2. インライン記述(style属性)

3.  IDセレクタ

4. クラスセレクタ・属性セレクタ・疑似クラス

5. 要素セレクタ、疑似要素

6. ユニバーサルセレクタ

詳細度低

 

①!important

!importantは入り札的な宣言です。プロパティの値の後ろに半角スペースに続けて記述することで、最も優先されるルールとなります。

 

!importantは基本的に避けたい記述です。もしこれが多用されている状態になっているということは、既存のCSSが高い詳細度のセレクタであふれてどうしようもない状態にあるときでしょう。これこそCSSの大きな破綻が始まっている兆しといえます。

ただし!importantを能動的、意図的に使うことは許容されることもあります。これについては第三章で解説します。

②インライン記述

インライン記述というのは、HTMLのstyle属性によるスタイルの記述のことです。

 

CSSファイルまたはHTML上でのstyle要素などに記述されたルールよりも優先されます。状況に寄りますが、基本的にはこの記述方法も避ける方が無難です。

詳細度の話を置いたとしても、スタイルは外部ファイル化されたCSSファイルで一元管理する方が管理上好ましいといえるので、そういった理由でもおすすめしません。

このように直接style属性でスタイルを記述するケースとして、デバッグのために一時的にスタイルを持たせたいとか、本当にその場所でしか使わないためにCSSファイルに記述したくない、といったことはあるかもしれません。

また、Javascriptによって、style属性に直接スタイルを書き込む場合もあります。例えば、jQueryプラグインなどが強制的に設定する任意のスタイルや、表示・非表示のためのdisplay:noneの操作と言ったものです。できればこうしたJavascriptの操作によるスタイルの変更も、スタイルのルール自体はCSSファイルに持たせて、Javascriptではクラスを付けたり外したりすることだけを行うといった方法を取るのが好ましいと考えています。

③IDセレクタ

IDはドキュメント(ページ)の中で一度のみ使うことができます。HTMLにおいてid=”foo”と降られた要素があったとすれば、そのfooというIDは同一ドキュメントでは一度しか利用することはできません。そうした固有の識別子であり、CSSのセレクタとしては、最も高い詳細度になります。

 

④クラスセレクタ、属性セレクタ、疑似クラス

IDより詳細度は低く、次に紹介する要素セレクタより高いのが、これらのセレクタです。このコードはクラスセレクタの例です。

 

IDとは異なり、class属性はドキュメント内で何度も使われていてもかまいません。またclass属性と同じレベルで、属性セレクタと疑似クラスセレクタの詳細度が並びます。属性セレクタは、要素の持つ属性の有無、または属性値と条件にマッチするかどうかで選択できます。

 

疑似クラスセレクタは、疑似クラスという名前の通り、条件にマッチした要素にクラスセレクタのレベルで、ルールが適用されます。

 

⑤要素セレクタ、疑似要素

要素を直接選択するセレクタは、IDやクラスよりも詳細度は低くなります。また::afterや::beforeと言った疑似要素も同等。

⑥ユニバーサルセレクタ

*を使ったセレクタは、すべての要素にマッチするセレクタです。ユニバーサルセレクタは詳細度を持たず、どのセレクタよりも弱いセレクタです。

すべての要素を対象とするという利点があるため、プロジェクトの初期スタイルをリセットする目的で使われることがありましたが、パフォーマンス(処理速度)の観点や、リセットが不都合となる要素も対象となってしまうなどの理由から、最近はその目的で使うことはあまり推奨されていません。

 

※詳細度の計算

各セレクタの優先順位は前述の通りですが、実際には、これらが組み合わさることによって、より複雑になります。例えば、1つのクラスセレクタと2つのクラスセレクタのルールセットがあった場合には、後者の方が優先されます。

 

単純に、各セレクタの持つ詳細度を数値化して、それらを合算する、というのではなく、それぞれの単位で詳細度を累積して高い方が優先されます。要素セレクタはどれだけの数があっても、1つのクラスセレクタより優先されることはありません。クラスセレクタはどれだけの数があっても、1つのIDセレクタより優先されることはありません。

クラスセレクタはどれだけの数があっても、1つのIDセレクタより優先されることはありません。同様に、数に関係なく、インラインや!importantが使われる場合は、それらが優先されます。

 

2-2.セレクタのリファクタリング

おそらくは多くの開発者がCSSの詳細度については知っているものの、自然とセレクタはどんどん多く、複雑になりがちです。そうなると、メンテナンスをする都度、既存のルールセットとそのセレクタの詳細度について検証することに多くの時間をとられてしまいます。それが続くと、いつしか!importantで切り抜ける道を選びがちですが、そうなる前にセレクタのリファクタリングを行うようにしましょう。

※セレクタをより安全でシンプルなものに

マークアップを考えて、その構造を元に素直にセレクタを書いていくと、セレクタは多く、複雑になります。その結果、セレクタの詳細度を高めてメンテナンス効率を下げる、というのは、ここまで解説した通りです。

多くの場合、そうしたHTMLに依存したセレクタをもっとシンプルに、そしてより安全なセレクタへと最適化することができます。ここでは、そうしたCSSセレクタのリファクタリングについて考えてみましょう。

※要素セレクタを省略する

よく要素セレクタとクラスセレクタの組み合わせを書くことがあります。その意図は、おそらくセレクタが表す通り、次のようなものかもしれません。

・タイトルとして協調するh2要素

・リンク一覧となるul要素

 

しかしこうした要素を指定することは詳細度を高めるだけにすぎず、セレクタとしては省略しても問題ないはずです。

 

もしマークアップとCSSの関係を明示したいのであれば、セレクタでそれを表すのではなく、コメントで表現する方が、セレクタの詳細度を上げずに、別の開発者に意図を伝えることができます。

 

ただし多くの場合、そうした要素を指定すること自体にもメリットはさほどなく、デザインやマークアップの変更によって要素が変わってしまうこともあるので、コード例にあるような「このクラスはh2要素に適用する」のようなガイドは不要かもしれません。実際には、h2要素でなくともh1要素やh3要素でも機能するようにすべきです。

※セレクタを短くする

セレクタは、マークアップの構造に従う必要はありません。例えばul要素の直下はli要素というのが、HTML5の仕様ですが、セレクタに置いて必ずそのように書かなければいけないわけではありません。

 

このようなルールセットがあったとすれば、少なくとも次のように省略できるでしょう。

 

これが本当にli要素の中のa要素のみに対するルールである、ということが意図的に必要であれば、もちろんもとのままでも構いません。

重要なことは、不要なセレクタは省略することによって、単純に詳細度の問題を解決するだけでなく、特定の要素への依存を減らすことで、可搬性(移植しやすさ)を高めることにもなります。

※セレクタを限定的にする

今度は詳細度とは異なる点での最適化です。

ここまでに登場したセレクタは基本的に「子孫セレクタ」といい、セレクタ間に半角スペースを空けて記述されるセレクタです。子孫セレクタは、「子要素以下すべての要素」つまり孫となる要素も含めて対象となります。

これに対し、>を使った子供セレクタでは「子要素」つまり直下の要素のみが対象となります。子孫セレクタが問題となるのは次のような例です。

 

この例ではIDセレクタである#sidebarの詳細度を引きずり、違う見た目のリンクリストである<div class=”links”></div>内のリストとリンク要素を作るために、セレクタを重ねてどうにかもとのスタイルを打ち消しながら、新しいスタイルを定義しています。

これはまさにすでに説明したようなバッドプラクティスである「スタイルの打ち消し」や、たかくなってしまった詳細度によるセレクタの冗長化の問題が発生しています。この例は、子供セレクタを使うことによって、解決することができます。

 

これによって、もとのコードよりは詳細度の問題や、ふようなスタイルの打ち消しなどは解決されています。さらにここからリファクタリングすることができます。

※くらすセレクタを活用する

ここまでの解説でわかる通り、IDセレクタが詳細度を高めてしまう要因となることは明らかです。先ほどの例を次のように、マークアップも含めて書き直してみるとどうでしょうか。

 

上部にあるメニューリストを.menusというクラスで命名し、クラスセレクタで書き直しました。このようにすることで、さらにIDセレクタを減らし、メンテナンスしやすいCSSとなりました。

実はこのリファクタリングで解決したのは、詳細度の問題だけではありません。もとのコードで問題だったのは、メニューリストのリンクが、#sidebarという場所に依存していたことにもありました。

例えばもとのコードが書かれていたときには2カラムのWebサイトで、メインコンテンツエリアとサイドバーエリアに分かれているレイアウトだとします。

ところが、このサイトの情報量が増えたり、ユーザーの閲覧環境を見直したりした結果、ページの幅を広げて3カラムと変更することになったために、もう1つサイドバーが増えたとします。

ここでもともと#sidebarというセレクタに依存しているメニューリストを、<div id=”navigation”></div>に移動させるとすれば、マークアップ上での変更はもちろん、CSS上でも#sidebarを#navigationに書き換えると言ったことをしなければならないかもしれません。

 

まず、これでCSS側の修正と言う手数が増えているわけですが、現実では、一度変更したものの、再び#sidebarに戻すとか、ID名を#sidebarAと#sidebarBにするといったことも起こりうるかもしれません。場所に依存したセレクタである限りは、場所は変われば使えなくなりますし、その都度書き換えなければいけません。

これに対し、リファクタリング後に.menusというクラスセレクタでまとめたほうのコードであれば、マークアップだけ置き換えれば、どの場所にあってもメニューリストの機能・体裁は変わりません。

こうした設計が、コンポーネント設計などと呼ばれ、まとまったルールセットを1つの部品として考えて設計する手法です。HTMLの構造に極力依存させずにすたいるを作ることによって、冒頭で上げた4つのゴールに近いCSSを書くことができます。

このコンポーネント設計をより具体的なコンセプトや手法としてどう進めるかを次の第3章から解説します。

 

チャプター3

コンポーネント設計のアイデア

CSSの再利用性や保守性を高めるために、コンポーネントを意識した設計を考える必要があります。それは、CSSのルールセットを取り換えの利く部品=コンポーネントにすることを意味しますが、決して容易ではありません。著名な開発者たちが考えた、実践するためのアイデアを見ていきましょう。

3-1CSSにおけるコンポーネント設計

CSSのコンポーネント設計に関する様々なアイデアを見ていく前に、コンポーネントそのものと、CSSにおけるコンポーネント設計の考え方について、ここまでの復習もかねて考えてみましょう。

コンポーネントとは?

コンポーネントは、もともとソフトウェア工学やその他の工学分野で扱われている用語です。コンポーネントがコンポーネントであるための前提として、関心の分離というものがあります。

厳密な定義で説明すると、少し難しいのですが、かみ砕いて言ってしまうと、機能や振る舞いなどを明確に分離をするという事です。

これはJavascriptの世界でもよく扱われるようになったMVCアーキテクチャによるModel(データ)とView(見た目)の分離や、本章のトピックであるHTMLのマークアップ構造とCSSのスタイリングの分離も、ひろい意味でとらえればその1つと言えるでしょう。

分離を実現するためには、カプセル化が重要です。カプセル化について簡単な説明をすると、カプセル化は、コンポーネントの構造やデータなどを隠し、外部からは許可された操作のみを受け付け、また内部の仕様変更が外部に影響しないようにする、と言ったことです。カプセル化により、あるコンポーネントの変更が、別のコンポーネントを壊してしまう、といったことを防ぎ、分離が成立します。その結果、コンポーネントのメンテナンス性が高まり、また他のコンポーネントとの不要な依存関係をなくすことで再利用性も高くなります。

しかし残念ながら現状のCSSにはカプセル化の概念がなく、容易にスタイルが予想外に他の要素に影響してしまうということがあります。

例えば、IDおよびIDセレクタを用いたルールセットは、その強固な詳細度によってルールがうわがきしづらくなっている点で、カプセル化を実現しているように見えるかもしれません。

 

しかし、それは大きな勘違いで、下記のようなルールセットが存在した場合には、詳細度に関係なく汚染されます。

 

理想のいえば#newsの中の.titleのルールが守られるように、スコープ(参照される範囲)を設けられればいいのですが、現状のCSSではすべてがグローバルなスコープです。そのため、これは極端な例ではありませんが、CSSを書く開発者が複数人存在する場合は、おのおのの意図で書いたルールがこのように汚染を発生させることがよく起こります。

このように、コンポーネントを設計を実現することがむずかしいCSSですが、次に紹介するオブジェクト指向CSS=OOCSSをベースにした、多くのCSSにおけるコンポーネント設計のアイデアが存在します。それがどういったアプローチであるかを見てみましょう。

 

3-2 OOCSS

コンポーネント設計がむずかしいCSSに対し、オブジェクト指向プログラミングの概念を取り入れて、その実現に近づけたのが、OOCSS(Object Oriented CSS、オブジェクト指向CSS)です。

OOCSSは、多くのCSS設計のアイデアの基礎となるもので、本書においても肝となる話です。

OOCSSは、元・米ヤフーのデベロッパーであり、現在はコンサルタントや世界各地での講演活動を行っているニコール・サリバンが提唱しました。

OOCSSにおけるオブジェクトというのは、繰り返されるビジュアルパターンを指し、「1つのまとまったHTML、CSSないしは、Javascriptで書かれた独立したスニペットとして再利用できるものであると定義しています。

図4のように、ページ上に存在する同じような機能で繰り返されるものがパターンであり、つまりはこれがコンポーネントという部品になります。彼女の例えで言い換えるならば、ブロック玩具を組み合わせて城や車を作るように、部品を組み合わせることでページを作り上げることができます。

OOCSSの原則

OOCSSの原則として彼女は、次の2つを挙げています。

・構造と見た目の分離

・コンテナとコンテンツを分離

これらの分離というのが、再利用可能なコンポーネントを設計する重要なキーワードです。

構造と見た目の分離

構造と見た目の分離というのは、コンポーネントの基本構造、具体的なルール・機能を分離するということです。

例えば、画面上にシステム的なアラートメッセージを表示するようなコンポーネントがあり、次のような種類が存在したとします。

 

 

コメントを残す

メールアドレスが公開されることはありません。