CLS

ページの読み込み中に発生する予期しないレイアウトのずれを測定する Core Web Vitals の指標

パフォーマンスSEO

CLS とは

CLS (Cumulative Layout Shift) は、ページの読み込み中や操作中に発生する予期しないレイアウトのずれ (要素の位置変動) の大きさを測定する Core Web Vitals の指標である。Google が 2020 年に導入し、検索の順位付けに使われる指標群の 1 つとして扱われている。

個々のレイアウトシフトのスコアは「影響範囲の割合 (impact fraction) × 移動距離の割合 (distance fraction)」で決まる。影響範囲の割合は、直前のフレームと当該フレームで位置が動いた要素が占めた可視領域を合わせたものが、ビューポート全体の面積に対して占める比率である。移動距離の割合は、動いた要素の水平 / 垂直方向の最大移動距離を、ビューポートの長い方の辺 (幅または高さ) で割った値である。したがって画面の大部分が大きく動けばスコアは急に悪化し、小さな要素がわずかに動くだけならほとんど加算されない。

名前に「累積 (Cumulative)」と付くが、2026 年 8 月時点の定義はページ滞在中の全シフトの単純合計ではない。間隔が 1 秒未満で連続して起きたシフトの一群をひとつのセッションウィンドウ (合計の長さは最長 5 秒) とみなし、ウィンドウ内でスコアを合計したうえで、ウィンドウごとの合計値のうち最大のものをそのページの CLS とする。この集計方法は 2021 年 4 月に変更案として公表され、同年 6 月に Chrome の計測ツール群へ反映された。長時間読まれるページほど小さなずれの積み上がりで際限なく悪化する、という初期定義の不公平を避けるための変更である。

スコア評価
0.1 以下良好 (Good)
0.1 超 0.25 以下改善が必要 (Needs Improvement)
0.25 超不良 (Poor)

実ユーザーのデータで良否を判断するときは、ページ読み込みの 75 パーセンタイル値をこのしきい値に当て、モバイルとデスクトップを分けて集計する。平均値で見ると、一部の環境だけで起きる大きなずれが埋もれてしまう。

なぜ CLS が問題なのか

記事を読んでいる最中に、広告が読み込まれてテキストが下にずれる。「購入」ボタンを押そうとした瞬間に、上に画像が表示されて「キャンセル」ボタンを押してしまう。これがレイアウトシフトだ。ユーザー体験を著しく損なう。

CLS の主な原因と対策

1. 画像 / 動画のサイズ未指定

<!-- ❌ サイズ未指定: 画像読み込み後にレイアウトがずれる -->
<img src="hero.jpg" alt="Hero image">

<!-- ✅ width/height を指定: ブラウザが事前にスペースを確保 -->
<img src="hero.jpg" alt="Hero image" width="1200" height="630">

<!-- ✅ CSS の aspect-ratio でも可 -->
<style>
  .hero-img { aspect-ratio: 16 / 9; width: 100%; }
</style>

2. Web フォントの FOUT/FOIT

Web フォントの読み込み中にシステムフォントで表示され、読み込み完了後にフォントが切り替わるとレイアウトがずれる。

/* font-display: swap + サイズ調整 */
@font-face {
  font-family: 'CustomFont';
  src: url('/fonts/custom.woff2') format('woff2');
  font-display: swap;
  size-adjust: 105%; /* システムフォントとのサイズ差を調整 */
}

<link rel="preload"> でフォントを先読みし、切り替えのタイミングを早める。ただし font-display: swap は表示の遅れ (FOIT) を避ける代わりにフォントの差し替えを必ず起こすため、先読みだけではずれは消えない。代替フォントと本番フォントで 1 行の高さと文字幅が揃うよう size-adjust などで字面を合わせておくことが、ずれを 0 に近づける決め手になる。

3. 広告枠のサイズ未確保

/* 広告枠の最小サイズを CSS で確保 */
.ad-slot {
  min-height: 250px; /* 広告の最小高さ */
  background-color: #f5f5f5;
}

4. 動的コンテンツの挿入

// ❌ ページ上部に動的にバナーを挿入 → 全コンテンツが下にずれる
document.body.prepend(createBanner());

// ✅ 固定の高さを持つコンテナを事前に配置
<div id="banner-slot" style="min-height: 60px;"></div>

CLS の測定方法

CLS はラボ (合成計測) とフィールド (実ユーザー計測) の両方で測る。ラボ計測はスクロールもクリックもしない条件で 1 回読み込むだけなので、遅れて挿入される広告や操作に伴うずれが再現されず、値が実態より小さく出やすい。改善したかどうかの判定はフィールドデータで行い、ラボ計測は原因の切り分けと修正前後の比較に使う。

# Lighthouse で測定
npx lighthouse https://example.com --only-categories=performance

# Chrome DevTools → Performance タブ → Layout Shift を確認
# PageSpeed Insights で実ユーザーデータ (CrUX) を確認
// JavaScript で CLS をリアルタイム測定
new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (!(entry as any).hadRecentInput) {
      console.log('Layout shift:', (entry as any).value);
    }
  }
}).observe({ type: 'layout-shift', buffered: true });

hadRecentInput が真のエントリは、直近 500 ミリ秒以内のユーザー操作をきっかけに起きたシフトである。クリックやキー入力に応じた変化は予期しないずれではないため、CLS の計算から外す。逆に言えば、操作から 500 ミリ秒以上たってから動いた要素は「予期しないずれ」として計上される。

なおこのコードが出力するのは個々のシフトのスコアであり、CLS そのものではない。セッションウィンドウ単位の集計を含んだ値が必要なら、Chrome チームが公開している web-vitals ライブラリの onCLS() を使うのが確実である。

Next.js / React での CLS 対策

  • next/image コンポーネントは自動的に width / height を設定し、CLS を防ぐ
  • next/font は Web フォントを最適化し、FOUT を最小化する
  • Suspense の fallback に固定サイズのスケルトン UI を表示する

CLS はデプロイ直後には気付きにくく、広告枠やサードパーティのウィジェットを 1 つ足した時点で静かに悪化する。埋め込みを追加するときは先に高さを確保し、リリース後もフィールドデータの推移を数週間追う運用にしておくと再発を早く捕まえられる。なお読み込みの速さ自体は LCP が受け持つ指標で、CLS とは原因も対策も別系統である。

共有:

この記事は役に立ちましたか?

関連用語

関連する記事