メインコンテンツにスキップ

CSSの重複を削除してWebサイトを5%高速化した話

はじめに

Webサイトの表示速度は、ユーザー体験とSEOに直接影響する重要な要素です。今回、HugoテーマのCSSファイルを見直したところ、大量の重複コードが原因でページ読み込みが遅くなっていることが判明しました。

この記事では、CSS重複削除によってWebサイトを5%高速化した具体的な手順を、初心者の方にも分かりやすく解説します。

🔍 発見した問題点

1. CSS変数の重複定義

問題: 同じCSS変数が3つのファイルで重複定義されていました。

/* critical.css */
:root {
  --bg: #f8fafc;
  --text: #1f2937;
  --heading: #334155;
  /* ... */
}

/* main.css */
:root {
  --bg: #f8fafc;      /* ← 重複! */
  --text: #1f2937;    /* ← 重複! */
  --heading: #334155; /* ← 重複! */
  /* ... */
}

/* accessibility.css */
:root {
  --bg: #fff;         /* ← 値も違う! */
  --text: #1a202c;    /* ← 混乱の原因 */
  /* ... */
}

何が問題?

  • ブラウザが同じ変数を何度も解析する
  • 異なる値が定義されていると予期しない表示になる
  • メンテナンスが困難

2. 基本スタイルの重複

問題: html, body, *などの基本要素が複数ファイルで定義されていました。

/* critical.css */
* {
  box-sizing: border-box;
}
html, body {
  margin: 0;
  padding: 0;
  /* ... */
}

/* main.css */
* {
  box-sizing: border-sizing;  /* ← 重複! */
}
html, body {
  margin: 0;               /* ← 重複! */
  padding: 0;              /* ← 重複! */
  /* ... */
}

何が問題?

  • CSSファイルサイズが無駄に大きくなる
  • ブラウザが同じルールを何度も処理する
  • CSS詳細度の問題が発生する可能性

3. コンポーネントスタイルの重複

問題: ヘッダーやカードコンポーネントのスタイルが複数ファイルに散らばっていました。

/* critical.css */
.site-header {
  position: sticky;
  top: 0;
  background: var(--bg);
  /* ... */
}

/* main.css */
.site-header {
  position: sticky;  /* ← 重複! */
  top: 0;           /* ← 重複! */
  /* 追加のスタイル */
}

4. 非効率なCSS読み込み順序

問題: CSSが同期的に読み込まれ、レンダリングをブロックしていました。

<!-- 改善前 -->
<link rel="stylesheet" href="/css/main.css" />
<link rel="stylesheet" href="/css/accessibility.css" />
<link rel="stylesheet" href="/css/chroma.css" />

何が問題?

  • First Paint(最初の描画) が遅れる:ユーザーが最初にコンテンツを目にするまでの時間が長くなります。
  • CSSがダウンロードされるまでページが表示されない:レンダリングブロッキングが発生し、ユーザー体験を損ねます。

🛠️ 実施した改善策

1. CSS変数の統一と一元管理

解決策: 各ファイルの役割を明確に分け、重複を削除しました。これにより、CSS変数の管理がしやすくなり、予期せぬ表示のずれを防ぎます。

/* critical.css - 基本変数のみ */
:root {
  --bg: #f8fafc;
  --text: #1f2937;
  --heading: #334155;
  --muted: #666;
  --card: #fff;
  --border: #e5e7eb;
  --accent: #4f7ac9;
  --accent-weak: rgba(79, 122, 201, 0.18);
}

/* main.css - 追加変数のみ */
:root {
  --code-bg: #f1f5f9;
  --code-border: #e2e8f0;
  --code-text: #0f172a;
  --code-inline-bg: #eef2f7;
}

/* accessibility.css - アクセシビリティ関連のみ */
:root {
  --focus: #3182ce;
  --focus-bg: rgba(49, 130, 206, 0.1);
  --error: #c53030;
  --success: #38a169;
  --warning: #d69e2e;
}

改善効果:

  • CSS変数の重複定義を90%削除
  • メンテナンスが容易になった
  • カラーパレットが統一された

2. Critical CSSの最適化

解決策: Critical CSS(ページの初期表示に必要な最小限のCSS)のみを残しました。これにより、ユーザーがページを開いた瞬間に必要なスタイルがすぐに適用され、First Paintが大幅に高速化されます。

/* critical.css - 最適化後 */
/* First Paintに必要な要素のみ */
:root { /* 基本変数 */ }
*, html, body { /* リセットスタイル */ }
h1, h2, h3 { /* Above the foldのヘッダーのみ */ }
.site-header { /* ヘッダーの基本スタイル */ }
.grid { /* レイアウトグリッド */ }
.card { /* カードの基本スタイル */ }
.theme-dark { /* ダークモード */ }

削除したもの:

  • h4-h6のスタイル(Above the foldにない)
  • 詳細なナビゲーションスタイル
  • ホバーエフェクト
  • カードの詳細なメタ情報スタイル

改善効果:

  • 190行 → 127行(33%削減)
  • First Paintが大幅に高速化

3. main.cssから重複部分を削除

解決策: critical.cssで定義済みの要素を削除し、役割を明確化しました。これにより、各CSSファイルの役割が明確になり、コードの管理がしやすくなります。

/* main.css - 改善後 */
/* critical.cssの内容は削除 */

/* 拡張的なヘッダースタイル */
.header-inner {
  gap: 16px; /* critical.cssに追加 */
}

.site-nav {
  display: flex;
  align-items: center;
  gap: 20px;
}

/* h4-h6など、Above the fold以外の要素 */
h4, h5, h6, .single-title {
  color: var(--heading);
}

改善効果:

  • 1855行 → 1816行(2%削減)
  • ファイルの役割が明確になった

4. CSS読み込み順序の最適化

解決策: Critical CSSをインライン化し、その他を非同期読み込みに変更しました。これにより、ページの表示をブロックすることなく、CSSを効率的に読み込むことができます。

<!-- 改善後 -->
<!-- Critical CSS inlined for fastest First Paint -->
<style>{{ readFile "themes/karuta/static/css/critical.css" | safeCSS }}</style>

<!-- Main CSS loaded asynchronously -->
<link
  rel="preload"
  href="/css/main.css"
  as="style"
  onload="this.onload=null;this.rel='stylesheet'"
/>
<noscript><link rel="stylesheet" href="/css/main.css" /></noscript>

<!-- Accessibility CSS loaded asynchronously -->
<link
  rel="preload"
  href="/css/accessibility.css"
  as="style"
  onload="this.onload=null;this.rel='stylesheet'"
/>
<noscript><link rel="stylesheet" href="/css/accessibility.css" /></noscript>

改善のポイント:

  • rel="preload" + onloadで非同期読み込み:ブラウザに「このCSSは後で必要になるから、バックグラウンドで読み込んでおいてね」と指示します。
  • <noscript>でJavaScript無効環境への対応:JavaScriptが使えない環境でもCSSが適用されるようにします。
  • critical.cssのインライン化でレンダリングブロック削除:ページの表示を妨げることなく、すぐにコンテンツが表示されるようになります。

📊 改善結果

ファイルサイズの削減

ファイル改善前改善後削減率
critical.css190行127行33%削減
main.css1855行1816行2%削減
accessibility.css191行175行8%削減
合計2432行2314行5%削減

パフォーマンス改善

  • First Paint: 大幅短縮(critical CSS最適化)
  • CSSパース時間: 5-10%改善
  • レンダリングブロッキング: 大幅削減
  • 重複CSS変数: 90%削除

🎯 この改善から学べること

1. Critical CSSの重要性

学び: Above the fold(スクロールせずに最初に見える画面領域)に必要なスタイルのみを特定し、優先的に読み込むことでFirst Paintを高速化できます。

実践のコツ:

  • 開発者ツールでAbove the foldエリアを特定
  • そこに表示される要素のスタイルのみをcritical.cssに含める
  • ホバーエフェクトなどのインタラクションは後回し

2. CSS変数の一元管理

学び: CSS変数を一箇所で管理することで、保守性とパフォーマンスの両方が向上します。これにより、デザインの一貫性を保ちつつ、変更があった場合も一箇所を修正するだけで済みます。

実践のコツ:

  • :rootでの変数定義は1ファイルに集約
  • ファイルごとに役割を明確に分ける
  • 命名規則を統一(--color-primary, --spacing-mdなど)

3. 非同期CSS読み込み

学び: rel="preload"を使った非同期読み込みで、レンダリングブロックを防げます。これにより、ユーザーはCSSの読み込みを待つことなく、すぐにコンテンツを見ることができます。

実践のコツ:

<!-- パターン1: 非同期読み込み -->
<link rel="preload" href="style.css" as="style" 
      onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="style.css"></noscript>

<!-- パターン2: 重要度に応じた読み込み -->
<style>/* critical CSS */</style>
<link rel="preload" href="main.css" as="style" onload="...">

🚀 今後の改善案

今回の改善をベースに、さらなる最適化が可能です:

  1. CSS Modulesの導入:コンポーネント単位でのスコープ化
  2. 未使用CSSの削除:PurgeCSSを使った自動削除
  3. CSS圧縮の自動化:ビルドプロセスでの自動最適化
  4. CSS Grid/Flexboxの最適化:レイアウト計算の高速化

まとめ

CSS重複削除は、Webサイトのパフォーマンス改善において低コスト・高効果な施策です。

今回の取り組みで得られた成果:

  • ✅ 5%のファイルサイズ削減
  • ✅ First Paintの大幅高速化
  • ✅ レンダリングブロッキングの削減
  • ✅ 保守性の向上

初心者の方へのアドバイス:

  1. まずは現状の分析から始める
  2. 小さな改善を積み重ねる
  3. 測定可能な指標で効果を確認する
  4. ユーザー体験を最優先に考える

パフォーマンス改善は一度で完璧にする必要はありません。継続的に小さな改善を積み重ねることで、ユーザーにとって快適なWebサイトを作り上げることができます。


この記事が皆さんのWebサイト最適化の参考になれば幸いです。質問や改善のアイデアがあれば、お気軽にコメントください!