12k
All articles

Project Wallace によるスタイルシートの監査

Project WallaceのCSS監査は、色、フォントサイズ、詳細度、重複、ファイルサイズを具体的な修正に変えます。

OpenReplay Team
OpenReplay Team
Project Wallace によるスタイルシートの監査

Project Wallace を使った CSS 監査とは、オンラインアナライザーにスタイルシートを貼り付けて、5 つの数値を読み取ることです。ユニークな色の数、ユニークな font-size の数、セレクタの最大詳細度(そのそばに並ぶ id と !important のカウントも含む)、宣言の総数とユニーク宣言数の差、そして非圧縮ファイルサイズと gzip ファイルサイズの対比です。それぞれの数値は、異なる変更を指し示しています。

3 年前から使い続けているスタイルシートなら、すでにドリフトが起きているのではないかと感じているはずです。足りないのは、チケットに書き込める数値です。「CSS が雑な気がする」では優先度は上がりません。「デザインでは 2 色と定義されているグレーを、実際には 6 色出荷している」なら上がります。

この記事では、小さなフィクスチャ用スタイルシート 1 つをアナライザーに通し、出力をメトリクスごとに読み解き、各所見をそれによって引き起こされるべき具体的な変更と対応づけていきます。扱うのは診断です。治療については続編の How to Organize CSS in Modern Web Projects で扱います。

要点

  • ブラウザはパースできない CSS や認識できない CSS を破棄してレンダリングを続行するため、スタイルシートはビルドを一度も失敗させることなくミスを蓄積していきます。
  • CSS を貼り付けたりアップロードしたりすると、Project Wallace は解析を自分のデバイス上の WebWorker で実行するため、スタイルシートがブラウザの外に出ることはありません。
  • 出荷されているユニークな色の数と、デザインが定義しているパレットとのギャップこそがデザインドリフトの計測可能な姿であり、修正手段はルールの削除ではなく、値をトークンへ統合することです。
  • スタイルシートの途中に現れる詳細度のスパイクは、末尾での高い最大値よりもコストが高くなります。それ以降のすべてのオーバーライドが、その水準まで登らなければならないからです。
  • 空ルールは、視覚的なリグレッションチェックが一切不要な唯一の監査修正です。重複宣言は削除前に判断が必要です。

なぜ CSS 監査はビルドが見つけられないものを見つけられるのか

パースできない、あるいは認識できない CSS 宣言に出会ったブラウザは、その宣言を破棄してページのレンダリングを続けます。だからこそ、スタイルシートは何年もの間、ビルドを一度も失敗させずにミスを蓄積できるのです。この回復動作は仕様で定められています。CSS Syntax Module Level 3 のエラーハンドリング規則のもとでは、途中まで構築された宣言は破棄され、パーサーは次のセミコロンまで読み飛ばし、そこから通常のパースを再開します。

その結果、CSS の破損は失敗のようには見えません。ドリフトのように見えるのです。3 番目のグレーから 2 ポイントだけずれた 4 番目のグレー、16px18px のステップの間に挟まった 17px の見出し、締め切りに追われて追加された id セレクタと、それに打ち勝つために後から足された !important。どれも何も壊しません。しかしどれも、次の変更を難しくします。

Project Wallace のアナライザーはどう実行するのか

Project Wallace CSS analyzer は 3 通りの入力を受け付けます。ウェブサイトの URL、アップロードしたファイル、そして直接貼り付けた CSS です。CSS を貼り付けたりアップロードした場合、処理はローカルで行われます。自分のデバイス上の WebWorker が解析を行い、貼り付けた内容はどこにも送信されません。この設計は 2021 年のアナライザーのリライトに由来します。URL モードの場合は、解析の前に必然的に対象サイトをネットワーク経由で取得します。

入力ページには “Prettify CSS?” のトグルがあり、そのそばにはこのオプションが数値をわずかに変動させるという注意書きが添えられています。比較するつもりのある実行すべてで、状態を 1 つ選んで固定しておきましょう。

以下がフィクスチャです。3 人のコントリビューター、2 年間、ヘッダーとカードのコンポーネントが 1 つずつ:

/* header.css — three contributors, two years */
#site-header {
  background: #f5f5f5;
  color: #333333;
  font-size: 16px;
}

#site-header .nav-link {
  color: #343434;
  font-size: 15px;
  padding: 8px 12px;
}

.nav-link:hover {
  color: #222222 !important;
}

.card {
  background: #f4f4f4;
  color: #333333;
  font-size: 1rem;
  padding: 16px;
}

.card .card-title {
  font-size: 18px;
  color: #333333;
}

.card--featured .card-title {
  font-size: 17px;
  font-weight: 700 !important;
}

.legacy-banner {
}

.footer {
  background: #f5f5f5;
  color: #444444;
  font-size: 14px;
}

返ってくるのは、メトリクスのドキュメントと同じカテゴリにグループ分けされた結果ページです。Stylesheet、Atrules、Rules、Selectors、Declarations、Properties、Values。メトリクスは 100 を大きく超える数があります。以下の 5 つは、コミットにつながるものです。

ユニークな色と font-size は何を教えてくれるのか

色の総数とユニークな色の数の差は、各色がどれだけ再利用されているかを教えてくれます。そしてユニークな色の数とデザインシステムが定義するパレットとの差は、コードがデザインからどれだけドリフトしたかを教えてくれます。アナライザーは両方をカウントし、検出したユニークな色の完全なリストを提示します。font-size も同じ扱いです。

手作業で読み解くと、フィクスチャには 6 つの異なる 16 進文字列が含まれています。サーフェス用の #f5f5f5#f4f4f4、テキスト用の #333333#343434#222222#444444 です。このコンポーネントのデザインシステムは、ほぼ確実にサーフェス 1 色とテキスト 2 色を意図していたはずです。タイプスケールはさらに深刻です。16px15px1rem18px17px14px は記述としては 6 つの値であり、しかも 16px1rem は通常同じピクセルサイズに解決されます。

修正手段は削除ではなく統合です。ほぼ同一のグレーを統合するというのは、ルールを取り除くことではありません。各リテラルを最も近いトークンに置き換え、ルールには本来の役目をそのまま果たさせることです:

:root {
  --color-surface: #f5f5f5;
  --color-text: #333333;
  --color-text-muted: #444444;
  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.125rem;
}

.card {
  background: var(--color-surface);
  color: var(--color-text);
  font-size: var(--font-size-base);
  padding: 16px;
}

3 つのカラートークンが 6 つのリテラルを置き換え、3 つのサイズトークンが 6 つを置き換えます。Project Wallace の独立した Design Tokens ツールは既存の CSS から候補となる色と font-size を抽出してくれるため、実際のスタイルシートでリストを手作業で読むよりも素早く着手できます。

詳細度: 最大値よりスパイクが重要

スタイルシートの末尾近くにある高詳細度のセレクタはローカルな問題ですが、中央にあるものは、それをオーバーライドする必要のある以降のすべてのルールを同じ水準まで登らせます。そしてその登坂こそが、id セレクタと !important フラグが増殖する仕組みです。Harry Roberts の詳細度グラフは同じ主張を視覚的に示しています。トレンドは末尾に向かって緩やかに上昇すべきであり、スパイクはすべてその後のものが払うコストになります。

アナライザーは詳細度を id、class、type の 3 要素の値として報告します。Maximum selector specificity、Total selectors having maximum specificity、Top specificity selectors、そして Total id selectors、Total !important declarations、Ratio of !important declarations が並びます。

フィクスチャはこの仕組みをミニチュアで示しています。#site-header .nav-link は (1,1,0) に位置します。後続の .nav-link:hover は (0,2,0) でこれに勝てないため、コントリビューターは !important に手を伸ばしました。id セレクタ 1 つが !important フラグ 1 つを生み、.card--featured .card-title の 2 つ目のフラグは、そもそも font-weight を設定していないルールに勝とうとしています。

id セレクタのカウントと !important のカウントは、チームが 1 コミットずつ減らし、変更ごとに再計測できる 2 つの詳細度指標です。このフィクスチャでは、マークアップを id="site-header" から class="site-header" に変えるだけで、ヘッダーの 2 つのルールはどちらも (0,1,0) と (0,2,0) に平坦化され、2 つの !important フラグはどちらも不要になります。

重複宣言と空ルール

空ルールはバイト数とセレクタのマッチを生みますが、ユーザーに見えるものは何も変えません。そのため、これを取り除くことは視覚的なリグレッションチェックが一切不要な唯一の監査修正です。重複宣言は事情が異なります。同じ意図が二度書かれているのは臭いですが、間違ったコピーを削除するとカスケードが変わってしまいます。

アナライザーは Total empty rules を直接カウントします。フィクスチャでは .legacy-banner {} が唯一の事例で、これは削除対象です。重複宣言そのものを示す単独のメトリクスはありません。Total declarations を Total unique declarations と対比して読み、その差を重複数として扱いましょう。ただしドキュメントでは、空白や整形の違いによって 2 つの宣言が別物になるのかどうかは、いまだに未確定のままである点に留意してください。

フィクスチャでは color: #333333 が 3 つのルールに登場します。正しい対処は 2 つのコピーを削除することではなく、3 つすべてを var(--color-text) 経由にすることです。そうすれば、この反復は偶然ではなく共有された決定として可視化されます。

なぜ gzip ファイルサイズは肥大化したスタイルシートを隠すのか

gzip は反復を効率よく圧縮するため、重複宣言だらけのスタイルシートでも見栄えのよい圧縮サイズを示せてしまいます。その一方で、ブラウザが実際にパースするバイト数である非圧縮サイズは増え続けます。アナライザーは Uncompressed filesize、Gzip filesize、Gzip filesize compression ratio をまとめて報告します。

圧縮率の上昇が手がかりです。これはスタイルシートが小さくなっているのではなく、より反復的になっていることを意味します。非圧縮サイズを動かすのはルールと宣言の数であり、だからこそ上述の統合作業は副作用としてこれを削減します。ファイルサイズは他の修正のあとに読む診断指標であり、それ自体を最適化の目標にするものではありません。

リリースをまたいだスタイルシートの追跡

リリースをまたいでスタイルシートを追跡するには、各リリースの生の CSS をそのアナライザー結果と並べて保存し、すべての解析を同じ Prettify 状態で再実行します。CSS Diff viewer は貼り付けた 2 つのスタイルシートを整形し、ブラウザ内で行ごとに比較してくれるため、ユニークな色の数や id セレクタの数がどこで動いたのかが分かります。

まとめ

スタイルシート監査が時間に見合う価値を生むのは、各数値が変更に対応づけられているときです。ユニークな色と font-size はトークンへ、id セレクタと !important フラグはより平坦な詳細度へ、空ルールは削除へ、重複は共有宣言へ、そしてファイルサイズは残りの修正がうまくいったかの確認へ。本番で最も大きなスタイルシートをアナライザーに貼り付け、その 5 つの数値を書き留め、数値ごとに 1 つのプルリクエストを出しましょう。

FAQ

Project Wallace のアナライザーをコマンドラインや CI から実行できますか?

できます。wallace-cli という npm パッケージが、同じアナライザーをターミナルで実行します。npm install wallace-cli でインストールし、wallace path/to/styles.css を実行するか、stdin から CSS をパイプで渡します。CI スクリプト向けに機械可読な出力が必要な場合は --json フラグを追加してください。CLI のバージョン 4.x は Node 20.12 以降を必要とします。プログラムから利用する場合は、@projectwallace/css-analyzer から analyze 関数を import します。これは ESM 専用パッケージで、Node とブラウザの両方で動作します。

解析すべきは Sass や Tailwind のソースファイルですか、それともコンパイル済みの CSS ですか?

Sass、Less、Tailwind のソースではなく、ユーザーが受け取るコンパイル済み CSS を解析してください。変数、mixin、@extend はビルド時に展開されるため、ソースファイルではユニークな色、セレクタ数、ファイルサイズが誤って報告されます。Project Wallace 自身の Stylelint プラグインも、どこに向けるべきかについて同じことを述べています。出荷するバンドルに向けましょう。ユニークな値のカウントとそれを基に構築された比率は、ソースではなく配信されるファイルを記述するものだからです。URL モードはすでに、サイトが配信するビルド済みスタイルシートを取得しています。

Project Wallace CSS Analyzer と CSS Code Quality ツールの違いは何ですか?

CSS Analyzer は生のメトリクスを報告します。CSS Code Quality ツールはその出力を受け取り、独自のチェックを走らせ、結果を Performance、Maintainability、Complexity の 100 点満点 3 スコアに集約します。ある数値を特定のルールまで追跡したいときはアナライザーを、チームで共有できる主張のあるサマリーが欲しいときは Code Quality を使いましょう。どちらも URL、アップロードファイル、貼り付けた CSS を受け付けます。

監査の後、ユニークな色や詳細度が再び悪化するのを防ぐにはどうすればよいですか?

Stylelint の設定に @projectwallace/stylelint-plugin を追加してください。同じ解析エンジンの上に構築された 60 以上のルールが同梱されており、projectwallace/max-unique-colors も含まれます。その holistic プリセットは 1 ノードずつチェックするのではなく、ファイル全体(合計値、平均値、比率、ユニーク性)として判定します。recommended プリセットなら extends の 1 行で始められます。CI でビルド済みの CSS バンドルに対して実行し、7 番目のグレーを追加するプルリクエストがマージ前に失敗するようにしましょう。

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — self-hosted, with full data ownership.

Star on GitHub

We use cookies to improve your experience. By using our site, you accept cookies.