CLS完全攻略ガイド 2026年版 — レイアウトシフトをゼロに近づける実践テクニック

CLSの原因分析からデバッグ手法、2026年最新のCSS Containment、bfcache、フォントメトリクスオーバーライドまで、レイアウトシフトをゼロに近づける実践テクニックをコード例付きで解説。

はじめに:なぜ2026年にCLS対策が見落とされがちなのか

Core Web Vitalsの3指標のうち、CLS(Cumulative Layout Shift)は「合格率が最も高い」指標として知られています。実際、2026年初頭のCrUXデータでは約75%のサイトがCLSの基準(0.1未満)をクリアしている。LCPやINPと比べると、だいぶ楽勝に見えますよね。

でも、ここに落とし穴があります。

残りの25%のサイト ― つまり4サイトに1サイトは依然としてCLSで失敗しています。しかもCLSの厄介なところは、開発者のローカル環境では再現しにくいという点。高速な回線、ハイスペックなマシン、キャッシュが効いた状態ではレイアウトシフトはほぼ起きません。ところが実際のユーザー ― 3G回線のスマホ、初回アクセス、広告が読み込まれるタイミング ― ではガタガタとページが揺れ動いているかもしれないのです。

正直なところ、筆者自身もCLSの問題に気づくのが遅れた経験があります。開発中はまったく問題なかったのに、Search ConsoleでCLSの警告が出て慌てて調査したら、広告スロットとWebフォントのダブルパンチだった、ということがありました。

さらに厄介なのは、CLSがユーザーの「怒りクリック(Rage Click)」に直結するということ。購入ボタンを押そうとした瞬間にページがズレて、うっかり別のリンクをタップしてしまった ― そんな経験、あなたにもあるのではないでしょうか。Googleはこの「Rage Click」をユーザー体験の重要なシグナルとして捉えており、CLS悪化はSEOランキングに確実に響きます。

この記事では、シリーズの他の記事(INP最適化LCP/画像最適化TTFB/バンドル最適化)と合わせてCore Web Vitalsを完全制覇するために、CLSの原因分析からデバッグ手法、そして2026年に使える最新テクニックまでを徹底解説していきます。コード例もたっぷり載せているので、そのまま実装に使ってください。

CLSの基本:スコアの仕組みと閾値を正しく理解する

まずは基本から押さえていきましょう。ここを理解しておかないと、対策の優先順位がつけられません。

CLSスコアの計算方法

CLSスコアは、ページのライフサイクル全体で発生するすべての予期しないレイアウトシフトを記録し、「セッションウィンドウ」方式で最大バーストを計算します。計算式はシンプルです:

Layout Shift Score = Impact Fraction × Distance Fraction

  • Impact Fraction(影響割合):シフト前後の要素が占めるビューポート面積の割合
  • Distance Fraction(移動割合):要素がビューポートに対してどれだけ移動したかの割合

たとえば、ビューポートの50%を占める要素が25%分だけ下にズレた場合、Impact Fractionは0.75(元の位置50% + 移動先25%)、Distance Fractionは0.25となり、スコアは 0.75 × 0.25 = 0.1875 です。たった1回のシフトでこのスコア。なかなか衝撃的じゃないですか?

評価基準

  • 良好(Good):0.1未満
  • 要改善(Needs Improvement):0.1〜0.25
  • 不良(Poor):0.25以上

覚えておきたいのは、ユーザー操作から500ミリ秒以内に発生したレイアウトシフトはCLSスコアから除外されるということ。ボタンクリック後にメニューが展開される、といった「期待されるシフト」はカウントされません。問題になるのは、あくまでユーザーが予期しないタイミングで起きるシフトだけです。

CLSが悪化する6つの主要原因

では、具体的に何がCLSを悪化させるのか。ここからが本題です。

原因1:サイズ未指定の画像・動画

CLSの最大の原因は、widthheight属性が指定されていない画像です。HTTP Archiveのデータによると、全ページの66%に少なくとも1つのサイズ未指定の画像が存在しています。ブラウザは画像のダウンロードが完了するまで実際のサイズがわからないため、最初は高さ0pxで描画し、画像が読み込まれた時点でレイアウトが大きくズレます。

これ、本当に多い。個人的にはCLSの問題の半分以上がこれに起因していると感じています。

原因2:Webフォントの読み込み(FOUT/FOIT)

カスタムフォントが読み込まれると、フォールバックフォントとの差異によってテキストのサイズが変わり、レイアウトシフトが発生します。パターンは2つ:

  • FOIT(Flash of Invisible Text):テキストが一瞬見えなくなる
  • FOUT(Flash of Unstyled Text):フォールバックフォントで表示された後にカスタムフォントに切り替わる

特にfont-display: swapを使っている場合、フォールバックフォントとカスタムフォントの行の高さやカーニングが大きく異なると、テキスト全体が再配置されます。日本語フォントはファイルサイズが大きいので、この問題がより顕著に出やすいんですよね。

原因3:動的に挿入される広告・ウィジェット

広告はCLSの最大の加害者の一つです。広告ネットワークは動的なサイズの広告を配信することが多く、広告が読み込まれるタイミングで周囲のコンテンツが押し下げられます。特にファーストビュー(Above the fold)に配置された広告スロットは、大量のコンテンツを一気に押し下げるため、CLSへの影響が甚大です。

原因4:動的コンテンツの遅延読み込み

JavaScriptで非同期にAPIデータを取得して表示する場合、レンダリング完了後にDOM要素が追加されてレイアウトが変化します。Cookieバナー、チャットウィジェット、通知バーなど、ページ上部に後から挿入されるUI要素は特に大きなシフトを引き起こします。

原因5:iframe・埋め込みコンテンツ

YouTube動画、Googleマップ、SNS埋め込みなどのiframeは、読み込み完了まで実際のサイズがわかりません。スペースが予約されていなければ、読み込まれた瞬間にレイアウトが大きくズレます。これも見落としがちなポイントです。

原因6:CSSアニメーションの不適切な使用

heightwidthtopleftなどのレイアウトプロパティをアニメーションすると、ブラウザはフレームごとにレイアウトを再計算するため、レイアウトシフトが発生します。意外と知らない開発者が多い原因です。

CLSの測定とデバッグ:原因を正確に特定する

原因がわかったところで、次は「自分のサイトでどこが問題なのか」を特定する方法です。ここが実は一番重要かもしれません。

PerformanceObserverでリアルユーザーデータを取得する

Layout Instability APIを使えば、実際のユーザー環境でどの要素がどれだけシフトしたかを正確に記録できます。以下のコードは、CLSの原因要素を特定するための実装例です:

// CLS監視とソース要素の特定
let clsValue = 0;
let clsEntries = [];

const observer = new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    // ユーザー操作起因のシフトは除外
    if (!entry.hadRecentInput) {
      clsValue += entry.value;
      clsEntries.push(entry);

      // シフトの原因要素を特定
      entry.sources?.forEach((source) => {
        console.log('シフト要素:', source.node);
        console.log('移動前:', source.previousRect);
        console.log('移動後:', source.currentRect);
      });
    }
  }
});

observer.observe({ type: 'layout-shift', buffered: true });

このコードを本番環境に仕込んでおけば、ラボテストでは見つからない実ユーザー環境のレイアウトシフトを可視化できます。手間はかかりますが、やる価値は確実にあります。

web-vitalsライブラリでCLSを収集する

もっと手軽にやりたいなら、Googleのweb-vitalsライブラリがおすすめです。より簡潔にCLSの測定と原因分析ができます:

import { onCLS } from 'web-vitals/attribution';

onCLS((metric) => {
  console.log('CLS:', metric.value);

  const attribution = metric.attribution;
  console.log('最大シフト要素:', attribution.largestShiftTarget);
  console.log('最大シフト値:', attribution.largestShiftValue);
  console.log('最大シフト時刻:', attribution.largestShiftTime);

  // 分析サービスに送信
  sendToAnalytics({
    name: 'CLS',
    value: metric.value,
    target: attribution.largestShiftTarget,
    shiftValue: attribution.largestShiftValue,
  });
});

Chrome DevToolsでの視覚的デバッグ

Chrome DevToolsには、レイアウトシフトを視覚的にデバッグする機能が複数用意されています:

  1. Performanceタブ:レコーディングを開始してページをリロードすると、タイムライン上に「Layout Shift」イベントが表示されます。クリックすると、どの要素がどれだけ移動したかの詳細が確認できます
  2. Layout Shift Regions:DevToolsの「More tools」→「Rendering」を開き、「Layout Shift Regions」にチェックを入れると、レイアウトシフトが発生した領域が青色でハイライト表示されます
  3. Lighthouseタブ:パフォーマンス監査で「Avoid large layout shifts」の警告が出ると、CLSの原因要素が一覧表示されます

個人的には、まずLayout Shift Regionsで視覚的にざっと確認してから、Performanceタブで詳細を掘るのが効率的だと思っています。

実践的なデバッグフロー

CLSの原因を効率的に特定するための推奨フローはこうです:

  1. PageSpeed Insightsでフィールドデータ(CrUX)とラボデータの両方を確認
  2. CLSが0.1を超えていたら、Chrome DevToolsのPerformanceタブで詳細を調査
  3. Layout Shift Regionsを有効にして、実際にどの領域がシフトしているかを視覚的に確認
  4. web-vitalsライブラリを本番環境にデプロイして、リアルユーザーデータを収集
  5. 収集データから最も影響の大きいシフト要素を特定し、以降のセクションで解説するテクニックで修正

焦らず一つずつ潰していくのがコツです。

対策1:画像・動画のサイズを確実に指定する

基本中の基本 ― width/height属性

最もシンプルかつ効果的なCLS対策は、すべての<img><video>widthheight属性を指定することです:

<!-- NG: サイズ未指定 → CLSの原因 -->
<img src="hero.webp" alt="ヒーロー画像">

<!-- OK: サイズ指定あり → ブラウザがスペースを事前確保 -->
<img src="hero.webp" alt="ヒーロー画像" width="1200" height="630">

「そんなの知ってるよ」と思うかもしれませんが、実際にチェックしてみると抜けているケースは本当に多い。モダンブラウザは、widthheight属性からアスペクト比を自動計算し、CSSでwidth: 100%を指定しても正しいスペースを確保してくれます。

CSSのaspect-ratioプロパティを活用する

レスポンシブ画像では、CSSのaspect-ratioプロパティが非常に便利です:

/* レスポンシブ画像のCLS対策 */
img {
  max-width: 100%;
  height: auto;
}

/* 動画埋め込みのアスペクト比を保持 */
.video-container {
  aspect-ratio: 16 / 9;
  width: 100%;
  background-color: #f0f0f0;
}

/* 正方形サムネイル */
.thumbnail {
  aspect-ratio: 1 / 1;
  width: 200px;
  object-fit: cover;
}

aspect-ratioは画像だけでなく、動画コンテナやiframeにも使えるため、幅が可変のレスポンシブレイアウトでも表示後のズレを効果的に防止できます。昔はpadding-topハックで対応していましたが、もうその必要はありません。

対策2:Webフォントのレイアウトシフトを撲滅する

フォント周りのCLS対策は、特に日本語サイトでは避けて通れない課題です。

font-displayの選択肢を整理する

Webフォントによるレイアウトシフトを防ぐには、font-displayプロパティの正しい理解と、フォールバックフォントのメトリクス調整が鍵になります:

  • font-display: swap:フォールバックを即座に表示 → カスタムフォント到着時にスワップ(FOUTが発生、CLSリスクあり)
  • font-display: fallback:短いブロック期間(約100ms)後にフォールバック表示 → スワップ期間を限定
  • font-display: optional:初回描画でフォントが間に合わなければフォールバックのまま → CLSゼロ

CLS改善を最優先にするなら、font-display: optionalが最も安全です。ただし、初回訪問時にカスタムフォントが表示されない可能性がある点はトレードオフとして理解しておいてください。

フォールバックフォントのメトリクスオーバーライド

font-display: swapを使いつつCLSを抑えたい場合は、フォールバックフォントのメトリクスをカスタムフォントに近づける方法が有効です:

/* カスタムフォントの定義 */
@font-face {
  font-family: 'NotoSansJP';
  src: url('/fonts/NotoSansJP-Regular.woff2') format('woff2');
  font-display: swap;
}

/* フォールバックフォントのメトリクスをカスタムフォントに近づける */
@font-face {
  font-family: 'NotoSansJP-Fallback';
  src: local('Hiragino Sans'), local('Yu Gothic');
  size-adjust: 99%;
  ascent-override: 98%;
  descent-override: 25%;
  line-gap-override: 0%;
}

body {
  font-family: 'NotoSansJP', 'NotoSansJP-Fallback', sans-serif;
}

size-adjustascent-overridedescent-overrideline-gap-overrideの4つのディスクリプタを組み合わせることで、フォールバックフォントとカスタムフォントのレイアウト差異を最小化し、スワップ時のシフトをほぼゼロにできます。数値の調整には試行錯誤が必要ですが、効果は大きいです。

フォントの最適化チェックリスト

  • WOFF2形式を使用:最も圧縮率が高く、すべてのモダンブラウザがサポート
  • サブセット化:日本語フォントは特にファイルサイズが大きいため、使用するUnicode範囲に限定する
  • preloadで事前読み込み<link rel="preload" href="/fonts/NotoSansJP.woff2" as="font" type="font/woff2" crossorigin>
  • @importは使わない:CSS内の@importはリクエストが直列化され、フォントの読み込みが遅延する
  • セルフホスティング:Google Fontsなどの外部依存を排除し、自サーバーから配信

対策3:広告スロットのスペースを事前確保する

広告がらみのCLS対策は、正直言って一番もどかしい部分です。広告ネットワーク側の挙動をこちらで完全にはコントロールできないので。とはいえ、できることはあります。

固定サイズのコンテナを用意する

広告によるレイアウトシフトを防ぐ一番確実な方法は、広告スロットのサイズを事前にCSSで確保することです:

/* 広告スロットのスペース確保 */
.ad-slot-leaderboard {
  min-height: 90px;   /* 728×90 のリーダーボード広告 */
  width: 100%;
  max-width: 728px;
  background-color: #f8f8f8;
  display: flex;
  align-items: center;
  justify-content: center;
}

.ad-slot-rectangle {
  min-height: 250px;  /* 300×250 のミディアムレクタングル */
  width: 300px;
  background-color: #f8f8f8;
}

/* 広告が返されなくても領域を維持(折りたたまない) */
.ad-slot-leaderboard:empty::after {
  content: '';
  display: block;
}

ポイントは、広告が返されなかった場合でもコンテナを折りたたまないこと。空のスペースが残るのは見た目上やや気になりますが、CLSを防ぐためにはこれがベストです。

Google Publisher Tag(GPT)の最適化

Google AdSenseやAd Managerを使っている場合、GPTの設定でCLSを最小化できます:

<!-- 広告スロットの定義(サイズを事前指定) -->
<div id="ad-top" style="min-height: 250px;">
  <script>
    googletag.cmd.push(function() {
      // 複数サイズを指定する場合、最大サイズでスペースを確保
      googletag.defineSlot('/12345/top-banner',
        [[728, 90], [970, 250], [320, 100]],
        'ad-top'
      ).addService(googletag.pubads());

      // シングルリクエストモードでまとめて取得
      googletag.pubads().enableSingleRequest();
      googletag.enableServices();
    });
  </script>
</div>

対策4:動的コンテンツとスケルトンUIの活用

スケルトンスクリーンで領域を確保する

非同期で読み込まれるコンテンツ(レビュー、コメント、関連商品など)は、スケルトンUIで領域を事前確保しておきましょう:

<!-- スケルトンUIの実装例 -->
<style>
  .skeleton {
    background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
    background-size: 200% 100%;
    animation: skeleton-loading 1.5s infinite;
    border-radius: 4px;
  }

  @keyframes skeleton-loading {
    0% { background-position: 200% 0; }
    100% { background-position: -200% 0; }
  }

  .skeleton-card {
    height: 200px;
    margin-bottom: 16px;
  }

  .skeleton-text {
    height: 16px;
    margin-bottom: 8px;
  }

  .skeleton-text.short {
    width: 60%;
  }
</style>

<div class="review-section">
  <!-- データ読み込み前はスケルトンを表示 -->
  <div class="skeleton skeleton-card"></div>
  <div class="skeleton skeleton-text"></div>
  <div class="skeleton skeleton-text short"></div>
</div>

ここで重要なのは、スケルトンの高さが最終的なコンテンツの高さと一致していることです。スケルトンと実コンテンツの高さが大きく異なると、スケルトンが消えた時点でまたシフトが発生してしまいます。本末転倒にならないよう注意してください。

Cookieバナー・通知バーの正しい実装

Cookieバナーや通知バーは、ドキュメントフローの外に配置するのが鉄則です:

/* CLSを引き起こすNG例 */
.cookie-banner-bad {
  /* ページ上部にバナーを挿入 → 全コンテンツが押し下がる */
  position: relative;
  width: 100%;
  padding: 16px;
}

/* CLSを防ぐOK例 */
.cookie-banner-good {
  /* position: fixed でドキュメントフローから外す */
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  padding: 16px;
  z-index: 1000;
  background: #fff;
  box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
}

シンプルですが、これだけでCookieバナー起因のCLSは完全に解消できます。

対策5:CSS ContainmentとContent-Visibilityを活用する

ここからは少し上級者向けのテクニックに入ります。

CSS containプロパティでレイアウトを隔離する

CSS containプロパティは、要素の内部レイアウト変更が外部に影響しないように「レイアウトの島」を作る機能です。2026年時点で全モダンブラウザがサポートしています:

/* カードグリッドの各カードにcontainmentを適用 */
.card {
  contain: layout;
  /* カード内部の変更が他のカードに波及しない */
}

/* より積極的な最適化 */
.sidebar-widget {
  contain: content;
  /* layout + paint の組み合わせ(推奨デフォルト) */
}

/* サイズが確定している要素にはstrictを使用 */
.fixed-banner {
  contain: strict;
  /* size + layout + paint — 最大限の最適化 */
  width: 728px;
  height: 90px;
}

パフォーマンス面でも効果は大きく、実測データではCSS Containment適用時のレンダリング処理が約54msなのに対し、未適用時は約732msというケースが報告されています。10倍以上の差ですね。

content-visibility: autoの正しい使い方

content-visibility: autoはオフスクリーンの要素のレンダリングをスキップするCSSプロパティで、初期レンダリングの高速化に非常に効果的です。ただし、使い方を間違えるとCLSが逆に悪化するという落とし穴があるので要注意:

/* NG: contain-intrinsic-sizeなしで使うとCLSが悪化する */
.section-bad {
  content-visibility: auto;
  /* 高さ0で計算され、スクロール時にシフト発生 */
}

/* OK: contain-intrinsic-sizeでプレースホルダーサイズを指定 */
.section-good {
  content-visibility: auto;
  contain-intrinsic-size: auto 500px;
  /* スクロール前は高さ500pxで計算される */
  /* auto キーワードにより、一度レンダリングされた後は実際のサイズを記憶 */
}

contain-intrinsic-sizeautoキーワードがポイントです。これにより、初回は指定した概算値(500px)が使われ、一度画面内に表示された後は実際のサイズがブラウザに記憶されます。再びオフスクリーンになった際、記憶されたサイズが使われるのでシフトが最小限に抑えられるというわけです。

対策6:bfcache(Back/Forward Cache)を最大限活用する

bfcacheがCLSに与える劇的な効果

bfcache(Back/Forward Cache)は、ブラウザの「戻る」「進む」ボタンによるナビゲーションでページを瞬時に復元する仕組みです。全ナビゲーションの10〜20%が戻る/進むナビゲーションと推定されており、ここを最適化する効果は思った以上に大きい。

bfcacheから復元されたページは:

  • TTFB ≒ 0:メモリ内のスナップショットから復元
  • FCP・LCP ≒ 即座:DOMもスタイルも適用済み
  • CLS = 0:画像もフォントも読み込み完了状態で復元されるため、レイアウトシフトが発生しない

Googleのレポートでは、bfcacheの普及が2022年にCLSの最大の改善要因だったと報告されています。つまり、対応しない手はないということです。

bfcache適格性を確保する

bfcacheの恩恵を受けるには、ページが適格条件を満たす必要があります。以下が主な阻害要因と対策です:

// NG: unloadイベントを使うとbfcacheが無効になる
window.addEventListener('unload', () => {
  // このイベントは使わない!
  sendBeacon('/analytics');
});

// OK: pagehideイベントを使う
window.addEventListener('pagehide', (event) => {
  if (!event.persisted) {
    // bfcacheに入らない場合のクリーンアップ
    sendBeacon('/analytics');
  }
});

// bfcacheからの復元時にCLSカウンターをリセット
window.addEventListener('pageshow', (event) => {
  if (event.persisted) {
    // bfcacheから復元された
    // CLS測定をリセットして再開
    console.log('bfcacheから復元されました');
  }
});

bfcache適格性のチェック方法

Chrome DevToolsの「Application」タブ → 「Back/forward cache」パネルで「Test back/forward cache」ボタンを押すと、ページがbfcache適格かどうかを即座に確認できます。不適格の場合は理由が一覧表示されるので、一つずつ対処していきましょう。

よくある不適格理由:

  • unloadイベントリスナーの使用
  • Cache-Control: no-storeヘッダーの設定
  • 閉じていないBroadcastChannel接続
  • アクティブなWebSocket接続
  • サードパーティスクリプトによるAPI使用

サードパーティスクリプトが原因でbfcacheが効かないケースは結構あるので、unloadリスナーを使っている古いスクリプトがないか一度チェックしてみてください。

対策7:CSSアニメーションをレイアウトシフトから切り離す

transformプロパティを使う

アニメーションでは、top/left/width/heightの代わりにtransformopacityを使いましょう。これらはGPUで処理されるため、レイアウトの再計算が発生せず、CLSに影響しません:

/* NG: レイアウトプロパティのアニメーション → CLSの原因 */
.slide-in-bad {
  animation: slide-bad 0.3s ease;
}
@keyframes slide-bad {
  from { left: -100px; }
  to   { left: 0; }
}

/* OK: transformアニメーション → CLSゼロ */
.slide-in-good {
  animation: slide-good 0.3s ease;
}
@keyframes slide-good {
  from { transform: translateX(-100px); }
  to   { transform: translateX(0); }
}

/* 要素の表示/非表示もtransformで */
.accordion-content {
  transform: scaleY(0);
  transform-origin: top;
  transition: transform 0.2s ease;
  overflow: hidden;
}
.accordion-content.open {
  transform: scaleY(1);
}

この置き換えは機械的にできるので、既存のアニメーションを一括でチェックして修正するのがおすすめです。

実践チェックリスト:CLS 0.1未満を達成するために

ここまで解説したテクニックを、実際のプロジェクトで適用する際のチェックリストとしてまとめました。プロジェクトに合わせて取捨選択してください:

  1. すべての画像・動画にwidth/heightを指定しているか
  2. レスポンシブ画像にaspect-ratioを使っているか
  3. Webフォントのfont-display戦略を決めているかoptional推奨)
  4. フォールバックフォントのメトリクスオーバーライドを設定しているか
  5. フォントをWOFF2形式でサブセット化・preloadしているか
  6. 広告スロットに固定サイズのコンテナを用意しているか
  7. 動的コンテンツにスケルトンUIを実装しているか
  8. Cookieバナー等をposition: fixedで配置しているか
  9. 既存コンテンツの上にコンテンツを挿入していないか
  10. CSS Containmentを適切な要素に適用しているか
  11. content-visibility: autocontain-intrinsic-sizeを併用しているか
  12. bfcacheの適格性を確認・確保しているか
  13. CSSアニメーションでtransform/opacityを使っているか
  14. web-vitalsライブラリで本番環境のCLSを継続監視しているか

全部いきなりやる必要はありません。まずは1番と3番から始めるだけで、大きな改善が見込めるはずです。

よくある質問(FAQ)

Q1: CLSスコアが0.1を超えているのに、Lighthouseでは問題が見つかりません。なぜですか?

Lighthouseはラボデータで測定するため、実際のユーザー環境と結果が食い違うことがあります。広告の読み込み、3G回線での遅延、特定デバイスでのフォントレンダリングなど、ラボでは再現しにくい要因がCLSを悪化させているケースが多いです。正確な状況を把握するには、PageSpeed InsightsのフィールドデータCrUXや、web-vitalsライブラリによるRUM(リアルユーザーモニタリング)を使いましょう。

Q2: font-display: swapとoptional、どちらを使うべきですか?

CLSを最優先にするならfont-display: optionalが最も安全です。ただし初回アクセスでカスタムフォントが表示されない可能性があります。swapを使う場合は、フォールバックフォントのsize-adjustやメトリクスオーバーライドを設定してシフトを最小化しましょう。日本語サイトではフォントファイルが大きいため、optionalを選ぶか、サブセット化 + preloadでswapのリスクを軽減するのが現実的です。

Q3: content-visibility: autoを使ったらCLSが逆に悪化しました。どうすればいいですか?

これは非常によくある失敗パターンです。content-visibility: auto必ずcontain-intrinsic-sizeとセットで使う必要があります。未指定だとオフスクリーン要素が高さ0で計算され、スクロール時に大きなレイアウトシフトが発生します。contain-intrinsic-size: auto 500px;のように、autoキーワード付きで概算の高さを指定してください。

Q4: サードパーティの広告スクリプトがCLSの原因になっている場合、どう対処すべきですか?

広告スロットに対して、想定される最大サイズでCSSのmin-heightを設定してスペースを確保しましょう。広告が返されなかった場合もコンテナを折りたたまないようにします。また、ファーストビューの広告スロットは特にCLSへの影響が大きいため、可能であればファーストビュー以下に広告を移動することも検討してみてください。

Q5: bfcacheが有効になっているか確認するにはどうすればいいですか?

Chrome DevToolsの「Application」タブ → 「Back/forward cache」パネルで「Test back/forward cache」ボタンをクリックしてください。不適格の場合、「Actionable」(修正可能)、「Pending Support」(将来対応)、「Not Actionable」(制御外)の3カテゴリで理由が表示されます。まずはActionableな項目から対処していくのが効率的です。

まとめ:CLSはユーザー体験の「信頼感」を左右する

CLSは数値としてはCore Web Vitalsの中で最も達成しやすい指標ですが、ユーザー体験への影響は侮れません。ページがガタガタ動くサイトは「信頼できない」「怪しい」という印象をユーザーに与えます。それは直帰率やコンバージョンに直結します。

本記事で解説したテクニック ― 画像サイズの明示、フォント読み込み最適化、広告スロットの事前確保、CSS Containment、bfcache対応 ― を組み合わせれば、CLSを確実に0.1未満に抑えることができるはずです。

完璧を目指す必要はありません。まずは画像のサイズ指定とフォント戦略の見直しから始めてみてください。それだけでもスコアは劇的に改善します。

Core Web Vitalsシリーズの他の記事(INP最適化、LCP/画像最適化、TTFB/バンドル最適化)と合わせて実践すれば、3指標すべてで「良好」を達成できるはずです。すべてをクリアしているサイトはバウンス率が24%低下するという実測データもあるので、取り組む価値は十分にあります。

著者について Editorial Team

Our team of expert writers and editors.