CSS パフォーマンス最適化

破綻する CSS

以前、CSS が破綻する原因を 破綻しにくい CSS 設計手法と命名規則 の記事で書きました。

破綻しにくい CSS の設計手法はいくつか考案されていますが、いずれも完璧ではありません。 多くのサイトでは、CSS が適切にメンテナンスされておらず、肥大化しています。 肥大化した CSS はブラウザのレンダリングパフォーマンスに悪影響を与えます。

本ページでは、CSS を最適化しパフォーマンスを向上させる方法を説明します。 ただし、CSS の最適化はデータを圧縮するためパフォーマンスは向上しますが、メンテナンス性は損なわれます。 そのため、メンテナンスを行う CSS ファイルと、HTML が読み込む CSS ファイルは別ファイルにすると良いでしょう。

CSS ファイルサイズの削減

CSS ファイルのサイズが大きくなると、読み込みに時間がかかる原因になります。 CSS ファイルはデザインに影響を与えずにファイルサイズを削減することができます。 CSS ファイルのサイズを削減するには、以下の方法があります。

  • Minify 化
  • 使われていないルールセットの削除
  • 重複しているルールセットの削除
  • ショートハンドプロパティ

Minify 化

Minify 化とは、処理に必要のないコメントや空白、改行を削除することでデータ量の削減を行うことです。 Minify 化するには CMS のプラグイン、オンラインサービス、プログラムのライブラリやモジュールなどがあります。 以下は Minify 化を行った例です。

/* Minify 化前 */
.hoge {
    margin: 10px 0 10px 0;
    padding: 15px 15px;
    background-color: #FFFFFF;
    border: 1px solid #E2E2E2;
    /* border-radius: 4px; */
}

/* Minify 化後 */
.hoge{margin:10px 0;padding:15px;background-color:#FFF;border:1px solid #E2E2E2}
Minify 化を行った例

使われていないルールセットの削除

CSS が適切にメンテナンスされていないと、使われていない古いルールセットが残り続けている場合があります。 不要なルールセットは、ブラウザに不要な計算をさせるため、パフォーマンスにも悪影響を与えます。 そのため、どのページでも使われなくなった古いルールセットは削除します。

使われていないルールセットを手動で調べるのは大変なので、UnCSS などのモジュールを利用すると良いでしょう。

重複しているルールセットの削除

重複しているルールセットを削除する作業は、使われていないルールセットの削除よりも難しい作業で、リスクを伴います。 最終的に適用されるルールセットがわからないと、必要なルールセットまで削除する可能性があります。 以下は、セレクタ内での重複しているパターンと、別のクラスで上書きしているパターンです。

/* セレクタ内での重複 */
.hoge {
    width: 100px;
    ... (中略) ...
    width: 200px;
}

/* .foo を .bar で上書き */
.foo { width: 10px; }
.bar { width: 20px; }
ルールセットが重複している例

上記は簡略化した例ですが、実際はもっと複雑な状態になっています。 複雑化した CSS をクリーンな状態に戻すのは、絡まりあった糸を解くようなもので、簡単な作業ではありません。

安易な解決策として !important を使った優先度の変更があります。 しかし、この方法は CSS をより複雑化させるためにアンチパターン、または最後の手段として知られています。

最終的に適用されるスタイルは "詳細度" を算出することによって決まります。 詳細度は、a, b, c の 3 つの数値の大きさによって決まります。 以下はセレクタを詳細度の小さい順に並べています。

  1. *a=0, b=0, c=0
  2. lia=0, b=0, c=1
  3. ul lia=0, b=0, c=2
  4. ul ol+lia=0, b=0, c=3
  5. h1 + *[rel=up]a=0, b=1, c=1
  6. ul ol li.reda=0, b=1, c=3
  7. li.red.levela=0, b=2, c=1
  8. #foobara=1, b=0, c=0
  9. #foobar:not(li)a=1, b=0, c=1

a, b, c は桁数のようなもので、a > b > c の関係になります。 詳細度が同じ場合は、後から宣言されたルールセットが優先されます。

重複しているルールセットの削除は、詳細度を考慮して行います。 ただし、手動で行うのは大変なので CSSCSS などのモジュールを利用すると効率的です。

ショートハンドプロパティ

CSS には複数のプロパティをまとめて指定できる "ショートハンドプロパティ" という方法があります。 ショートハンドで表現できるプロパティの種類が多いため本ページでは割愛しますが、ショートハンドを利用することでファイルサイズを削減できます。 以下はショートハンドプロパティで表現した例になります。

/* 最適化前 */
background-color: #000;
background-image: url(images/bg.gif);
background-repeat: no-repeat;
background-position: left top;

/* 最適化後 */
background: #000 url(images/bg.gif) no-repeat left top;
ショートハンドプロパティの例

ショートハンドプロパティは複数のプロパティを指定できますが、省略したプロパティには初期値が設定されます。 そのため、設定済みのプロパティを初期値で上書きするため、使用には注意が必要です。

クリーンな CSS に変換するモジュール

CSS ファイルサイズを最小にしたい場合、変換するエンジンがあります。 例えば、以下は "cssnano" というモジュールによって CSS ファイルを圧縮した例になります。

/* 最適化前 */
/* normalize selectors */
h1::before, h1:before {
    /* reduce shorthand even further */
    margin: 10px 20px 10px 20px;
    /* reduce color values */
    color: #ff0000;
    /* remove duplicated properties */
    font-weight: 400;
    font-weight: 400;
    /* reduce position values */
    background-position: bottom right;
    /* replace initial values */
    min-width: initial;
}
/* correct invalid placement */
@charset "utf-8";

/* 最適化後 */
@charset "utf-8";h1:before{margin:10px 20px;color:red;font-weight:400;background-position:100% 100%;min-width:0}
cssnano によって最小化した例

上記の例では、ショートハンドプロパティへの置き換え、重複したプロパティの削除、文字列の変換など、様々な最適化が行われています。 このように CSS をクリーンな状態にするモジュールはいくつもあります。 以下のリンクは、その一例になります。

CSS セレクタの最適化

CSS のセレクタはパフォーマンスに影響を与えます。 CSS のセレクタは、HTML ドキュメントの要素とマッチング処理を何回も行うため、効率的にマッチングできるセレクタの方がパフォーマンス的に有利になるからです。 CSS のセレクタは、一般的に以下の順番でパフォーマンスが良いとされています。

  1. ID (#hoge)
  2. Class (.hoge)
  3. Tag (div)
  4. Adjacent sibling (h2 + p)
  5. Child (li > ul)
  6. Descendant (ul li)
  7. Universal (*)
  8. Attribute ([type="text"])
  9. Pseudo-classes (a:hover)

セレクタは組み合わせた複合的な指定が可能ですが、複雑な指定ほどマッチング処理に時間がかかり、パフォーマンスに悪影響を与えます。 CSS セレクタの最適化を行うには ID や Class を使ってシンプルに定義します。

スタイルの計算では、各要素をすべてのスタイルとマッチングするかのチェックを行います。 そのため、計算コストの最悪なケースは要素数とセレクタ数の積に比例します。 複合的なセレクタでは計算コストが悪化するだけでなく、メンテナンス性も悪くなるため避けた方が無難です。

レンダリングの最適化

ブラウザがページを表示するまでにはいくつもの処理が行われています。 ここでは HTML ファイルと CSS ファイルがダウンロードされ、レンダリングされるまでの最適化について説明します。

ダウンロードされた HTML ファイルは "DOM (Document Object Model)" に変換され、CSS ファイルは "CSSOM (CSS Object Model)" に変換されます。 DOM と CSSOM には処理コストがあるため、レンダリングを最適化することで処理コストを改善することができます。 DOM と CSSOM の構築コストと処理コストは DevTools で計測することができます。

DevTools による構築コストと処理コストの計測
DevTools による構築コストと処理コストの計測

クロスブラウザ対応

CSS では新しいプロパティが次々に登場しています。 Chrome や Firefox などのモダンブラウザではその恩恵を受けやすく、従来に比べてデザインを柔軟に構築できます。 しかし、一方でレガシーブラウザを利用しているユーザもいるため、クロスブラウザ対応の問題はデザイナーの悩みのタネです。

ブラウザの種類やバージョンは多岐にわたるため、すべて対応することは不可能です。 一部のサイトではレガシーブラウザの動作確認を行わずにモダンブラウザのみを対象としています。 どこまで対応するかは、サイトの特性や動作確認のコスト次第になります。

本サイトでも、基本的にはモダンブラウザのみ動作確認を行っています。 どこまでのブラウザの種類に対応するかは、Google Analytics からユーザ環境の割合を見て判断しています。

新しいプロパティはレガシーブラウザは対応していないため、なるべく使い古されたプロパティを優先的に選択しています。

まとめ

CSS はメンテナンス、パフォーマンスチューニング、クロスブラウザ対応など、管理するだけでも大変な労力が必要になります。 おまけに、配信するサーバの性能やネットワーク帯域、レンダリングを行うブラウザ、ユーザの端末スペックなどを考慮すると CSS の最適化はそこまで考慮するべき作業ではないかもしれません。 そして、仮に CSS を最適化しても、目に見えるほどの大きなリターンが得られるわけでもありません。

しかし、本サイトでは労力に見合わない作業であっても、ユーザビリティに少しでも寄与できるのであれば取り入れていきたいと思います。 本記事がサイトを運営している方へ CSS 最適化の動機付けになればと思います。