CLS(Cumulative Layout Shift)とは何か?定義・計算・評価基準・改善策を実務ガイド

CLS(Cumulative Layout Shift)とは

CLS(Cumulative Layout Shift、累積レイアウトシフト)は、ユーザーがページを見ている間に発生する視覚的なレイアウトの「予期しないズレ」を定量化する指標です。Google が定める Core Web Vitals の一つで、ユーザー体験(UX)の一端を担う重要な指標として採用されています。ページ内の要素が不意に移動すると、誤タップや読了の妨げになり、ユーザビリティに悪影響を与えます。CLS はその影響の大きさと頻度を数値で表します。

CLS の計算方法(仕組み)

CLS は、ページライフサイクルで発生する「個々のレイアウトシフト」のスコアを合算して算出されます。各レイアウトシフトのスコアは、以下の 2 つの要素の積です。

  • インパクト・フラクション(impact fraction):視覚的に影響を受けた視界(viewport)内の面積割合(例:画面の 25% が影響を受けたなら 0.25)
  • ディスタンス・フラクション(distance fraction):移動距離をビューポートの最大次元(幅または高さの大きい方)で割った割合

つまり、レイアウトシフトのスコア = impact fraction × distance fraction です。ページ全体の CLS は、一定の「セッションウィンドウ(session window)」ごとに発生したシフトのスコアの合計のうち、最大値を採用します。セッションウィンドウのルールは次の通りです:

  • あるウィンドウは最初のレイアウトシフトから始まる
  • 直前のシフトから 1 秒以内に次のシフトが起きれば同じウィンドウに含める
  • 最初のシフトから 5 秒を超えるとウィンドウは終了する(最大ウィンドウ長は 5 秒)

この仕様により、短期間に集中して発生するズレを重視し、長時間にわたる小刻みなシフトが無制限に累積することを防いでいます(詳細は公式ドキュメント参照)。

評価基準(しきい値)

  • Good(良好):CLS ≤ 0.10
  • Needs Improvement(改善が必要):0.10 < CLS ≤ 0.25
  • Poor(不良):CLS > 0.25

これらの目安は Google の推奨値で、検索評価やユーザー体験の指標として利用されます。

よくある原因(代表例)

  • 画像や動画に幅・高さ(またはアスペクト比)が指定されていないため、読み込み時にサイズが決まりレイアウトがずれる
  • 広告や埋め込みコンテンツ(iframe 等)が読み込み後に挿入され、上のコンテンツを押し下げる
  • 遅延読み込み(lazy-loading)でプレースホルダーがなく、読み込み後に要素が生成される
  • 動的に DOM を挿入・差し替えする SPA(Single Page Application)でコンテンツの挿入位置を確保していない
  • ウェブフォントの読み込みによるフォント切り替え(FOIT/FOUT)で文字サイズや行高が変わる
  • アニメーションで top/left 等のレイアウトを変える手法を使用している(transform を使わない)

計測方法とツール

CLS を確認する方法はいくつかあります。実運用での「フィールドデータ」と、開発時に使う「ラボデータ(シミュレーション)」を使い分けることが重要です。

  • フィールド(実ユーザー)データ:Chrome User Experience Report(CrUX)、web-vitals ライブラリ、PerformanceObserver(layout-shift)を使って実際のユーザー環境での CLS を収集
  • ラボ(開発)データ:Lighthouse、PageSpeed Insights(ラボのシミュレーション実行)でロード時のレイアウトシフトを検査。ただしラボは限られたシナリオのため、フィールドと差が出る場合がある
  • Chrome DevTools:Performance パネルでレイアウトシフトのフレームを可視化して、どの要素が移動したかを確認可能

簡単な計測用スニペット(PerformanceObserver):

const po = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    // user input によるシフトは除外される(entry.hadRecentInput)
    if (!entry.hadRecentInput) {
      console.log('layout-shift', entry.value, entry);
    }
  }
});
po.observe({type: 'layout-shift', buffered: true});

また、Google が提供する web-vitals ライブラリを使うと簡単にフィールド CLS を集計できます(例:getCLS(callback))。

改善策(実践的な手順)

以下は具体的な対策リストです。優先順位を付けて取り組むと効果が出やすいです。

  • 画像・動画に width/height を指定する
    • HTML の width/height 属性を付けておく(レスポンシブなら CSS と組み合わせてアスペクト比を維持)
    • 現代的には CSS の aspect-ratio を使うと簡潔に空間を確保できる
  • 遅延読み込み(lazy-load)のプレースホルダーを用意する
    • 空きスペースを確保するプレースホルダー要素やスケルトンを事前にレンダリングする
  • 広告・埋め込みは固定サイズのコンテナを用意
    • 広告サイズが未確定な場合でも事前に最小の高さを reserve する
    • 広告配信による高さ変動が想定される場合は、外側コンテナに最大高さを設定する
  • 動的挿入は既存レイアウトを押し下げない位置に行う
    • ページ上部に要素を追加する場合は、予め空の領域を確保する
  • フォントによるズレ対策
    • @font-face に font-display: swap や optional を設定して FOIT を抑制
    • 主要フォントを preload して読み込みを早める(rel=preload)
    • システムフォントやフォールバックでレイアウト崩れが小さい組み合わせを検討する
  • アニメーションは transform と opacity を使う
    • top/left/width/height をアニメーションするとレイアウトシフトが発生する。代わりに transform(translate/scale)や opacity を使う
  • CSS による高さ確保や contain を利用する
    • 必要に応じて min-height を設定したり、コンポーネント単位で CSS containment を使って予期せぬレイアウト影響を抑える

SPA や広告運用時の注意点

シングルページアプリケーションではルーティングや非同期ロードでコンテンツが差し替わることが多く、その際に予期しないレイアウトシフトが起きやすいです。対処法は以下の通りです:

  • ページ遷移でのレイアウト差分を想定し、遷移元/遷移先での共通レイアウト要素の高さを固定化する
  • 遅延ロードするコンポーネントに占有スペースを持たせ、レンダリング時の押し下げを防ぐ
  • 外部スクリプト(広告ネットワークなど)は非同期で読み込みつつ、挿入先の最小領域を事前に確保する

測定上の落とし穴と実務的なポイント

  • ラボデータ(Lighthouse 等)とフィールドデータ(CrUX や実ユーザーの計測)は一致しないことがある。特にユーザーのスクロールや遅延イベントはラボで再現しづらい
  • ユーザー操作(クリックやスクロールなど)によって引き起こされたレイアウトシフトは基本的に CLS の対象外(PerformanceEntry の hadRecentInput により除外)だが、UX 的には依然問題となることがあるので別途検査する
  • 改善の効果を早く知りたい場合は、web-vitals をページに埋めて実ユーザーの CLS を集め、変更前後で比較するのが有効

まとめ

CLS は「視覚の安定性」を数値化する重要な指標で、画像や広告、フォント、動的コンテンツ挿入などが主な原因です。主要対策は「要素の表示領域を事前に確保する」「アニメーションを transform にする」「フォント読み込み戦略を整える」ことです。ラボだけでなく実ユーザーのデータ(CrUX や web-vitals)を利用して現場の課題を特定し、優先順位を付けて改善を進めることが重要です。

参考文献