Cumulative Layout Shift の最適化

レイアウトの急激な変化を防ぎ、ユーザー エクスペリエンスを改善する方法を学ぶ

Cumulative Layout Shift(CLS)は、Core Web Vitals の 3 つの指標のうちの一つです。ビューポート内での表示コンテンツの移動量と、影響を受ける要素が移動した距離を組み合わせて、コンテンツの不安定性を測定します。

レイアウト シフトはユーザーの気を散らすことがあります。たとえば、記事を読み始めたところで、突然ページ内のさまざまな要素が入れ替わり、気が散り、目的の場所をもう一度探さなければならなくなったとします。これは、ニュースを読んだり、[検索] ボタンや [カートに追加] ボタンをクリックしようとする場合など、ウェブ上でよく発生します。そのようなエクスペリエンスは、視覚的に不快でフラストレーションを感じます。多くの場合、別の要素が突然ページに追加されたか、サイズが変更されたため、表示可能な要素が強制的に移動されました。

優れたユーザー エクスペリエンスを提供するには、サイトへのアクセスの 75% 以上で CLS が 0.1 以下になるようにする必要があります。

良好な CLS 値は 0.1 未満、低い値は 0.25 を超え、その間はすべて改善が必要
適切な CLS 値は 0.1 以下です。低い値は 0.25 より大きくなります。

時間ベースの値である他のウェブに関する主な指標(秒単位またはミリ秒単位)とは異なり、CLS スコアは、コンテンツの量と範囲の計算に基づく単位なしの値です。

このガイドでは、レイアウト シフトの一般的な原因の最適化について説明します。

CLS が低下する最も一般的な原因は次のとおりです。

  • サイズのない画像。
  • サイズのない広告、埋め込み、iframe。
  • サイズのない広告、埋め込み、iframe など、動的に挿入されるコンテンツ。
  • ウェブフォント。

レイアウト シフトの原因を理解する

CLS の一般的な問題に対する解決策を検討する前に、CLS スコアと、その変化の原因を理解しておくことが重要です。

ラボのツールとフィールドでの CLS

Chrome UX レポート(CrUX)で測定された CLS が、Chrome DevTools や他のラボツールで測定した CLS と一致しないため、正しくないとデベロッパーから思われることがよくあります。Lighthouse などのウェブ パフォーマンス ラボツールでは、ページ全体の CLS が表示されない場合があります。これらのツールでは、通常はページを読み込むだけで一部のウェブ パフォーマンス指標が測定され、ガイダンスが提供されます(ただし、Lighthouse のユーザーフローを使用すると、デフォルトのページ読み込みの監査を超えて測定できます)。

CrUX は Web Vitals プログラムの公式データセットであり、そのため CLS はページの全期間にわたって測定されます。ラボツールで通常測定される最初のページ読み込み時だけでなく、

レイアウト シフトはページの読み込み中によく発生します。これは、ページを最初にレンダリングするために必要なすべてのリソースが取得されるためです。ただし、初期読み込み後にレイアウト シフトが発生する場合もあります。読み込み後のシフトの多くは、ユーザー インタラクションの結果として発生する可能性があるため、そのインタラクションから 500 ミリ秒以内に発生するシフトが予想されるため、CLS スコアから除外されます。

ただし、対象となるインタラクションがなかった場合には、その他の読み込み後の移動(ページをさらにスクロールして遅延読み込みコンテンツが読み込まれて移動が生じるなど)が含まれることがあります。読み込み後の CLS のその他の一般的な原因として、500 ミリ秒の猶予期間よりも長い時間がかかるシングルページ アプリなど、遷移のインタラクションが挙げられます。

PageSpeed Insights では、[実際のユーザー エクスペリエンスを確認する] セクションに URL からユーザーが認識した CLS が表示され、[パフォーマンスの問題を診断する] セクションにラボベースの負荷 CLS の両方が表示されます。これらの値の差異は、読み込み後の CLS の結果であると考えられます。

PageSpeed Insights のスクリーンショット。URL 単位のデータで、Lighthouse の CLS よりもかなり大きい実際のユーザー CLS がハイライト表示されている
この例では、CrUX は Lighthouse よりもはるかに大きな CLS を測定しています。

読み込みの CLS の問題を特定する

PageSpeed Insights の CrUX と Lighthouse の CLS スコアがおおむね一致している場合、通常は Lighthouse で検出された負荷の CLS の問題があることを示しています。この場合は、Lighthouse で 2 つの監査を実施して、幅と高さが欠落していることによる CLS の原因となる画像について、詳しい情報を提供します。また、ページの読み込み時にシフトしたすべての要素を CLS とともに一覧表示します。CLS 監査でフィルタすることで、これらの監査を確認できます。

CLS の監査結果を示す Lighthouse のスクリーンショット。CLS の問題の特定と対処に役立つ詳細情報が提供されています。
Lighthouse の詳細な CLS 診断。

DevTools の [パフォーマンス] パネルでも、[エクスペリエンス] セクションのレイアウト シフトがハイライト表示されます。Layout Shift レコードの [Summary] ビューには、レイアウト シフトの累積スコアと、影響を受ける領域を示す長方形のオーバーレイが表示されます。これは、再読み込みのパフォーマンス プロファイルで簡単に再現できるため、読み込みの CLS の問題の詳細を取得する際に特に役立ちます。

エクスペリエンス セクションを開くと、Chrome DevTools のパフォーマンス パネルに表示される Layout Shift レコード
[パフォーマンス] パネルで新しいトレースを記録すると、結果の [Experience] セクションに Layout Shift レコードを示す赤いバーが表示されます。レコードをクリックすると、この画像の「移動元」エントリや「移動先」エントリなどの詳細が表示され、影響を受ける要素の詳細を確認できます。

読み込み後の CLS の問題を特定する

CrUX と Lighthouse の CLS スコアが一致しない場合は、多くの場合、読み込み後の CLS を示します。このような変化は、フィールド データがないと追跡が困難です。フィールド データの収集については、フィールド内の CLS 要素を測定するをご覧ください。

Web Vitals の Chrome 拡張機能を使用すると、ヘッドアップ ディスプレイまたはコンソールで、ページを操作するときに CLS をモニタリングできます。コンソールでは、シフトした要素の詳細を確認できます。

この拡張機能を使用する代わりに、コンソールに貼り付けた Performance Observer を使用してレイアウト シフトを記録しながらウェブページをブラウジングすることもできます。

シフト モニタリングを設定したら、読み込み後の CLS の問題を再現できます。CLS は、ユーザーがページをスクロールしているとき、つまり遅延読み込みコンテンツが、専用のスペースを確保せずに完全に読み込まれるときによく発生します。ユーザーがポインタの上にポインタを置いたときにコンテンツがシフトすることも、読み込み後の CLS の一般的な原因です。これらのインタラクションのいずれか中にコンテンツが変わると、たとえ 500 ミリ秒以内に起こったとしても、予期しないものとしてカウントされます。

詳細については、レイアウト シフトをデバッグするをご覧ください。

CLS の一般的な原因を特定したら、Lighthouse のタイムスパン ユーザーフロー モードを使用して、レイアウト シフトを導入することで一般的なユーザーフローの回帰を防ぐこともできます。

現場の CLS 要素を測定する

現場での CLS のモニタリングは、CLS がどのような状況で発生しているかを判断し、考えられる原因を絞り込むうえで非常に重要です。ほとんどのラボ用ツールと同様に、現場のツールもシフトした要素のみを測定しますが、通常は原因を特定するための十分な情報を提供します。また、CLS フィールド測定を使用して、修正の優先度が最も高い問題を特定することもできます。

web-vitals ライブラリには、この追加情報を収集できるアトリビューション関数があります。詳細については、フィールドでパフォーマンスをデバッグするをご覧ください。他の RUM プロバイダも同様に、このデータの収集と提示を開始しました。

CLS の一般的な原因

CLS の原因を特定したら、問題の解決に取り掛かることができます。このセクションでは、CLS が発生する一般的な理由と、それを回避する方法について説明します。

サイズが指定されていない画像

画像要素と動画要素には、必ず widthheight のサイズ属性を含めます。または、CSS aspect-ratio などを使用して必要なスペースを予約します。このアプローチにより、画像の読み込み中にブラウザはドキュメント内に適切な量のスペースを割り当てることができます。

幅と高さが指定されていない画像。
幅と高さが指定されている画像。
画像にディメンションを設定した後の Cumulative Layout Shift の使用前後の影響を示す Lighthouse レポート
Lighthouse 6.0 における CLS の画像サイズ設定の影響

画像の width 属性と height 属性の履歴

ウェブが開発された初期のころ、デベロッパーは width および height 属性を <img> タグに追加して、ブラウザが画像の取得を開始する前にページに十分なスペースを確保していました。これにより、リフローと再レイアウトを最小限に抑えることができます。

<img src="puppy.jpg" width="640" height="360" alt="Puppy with balloons">

この例の widthheight には単位がありません。この「ピクセル」サイズにより、ブラウザはページのレイアウトに 640x360 の領域を予約します。実際のサイズが一致するかどうかにかかわらず、このスペースに合わせて画像が拡大されます。

レスポンシブ ウェブ デザインが導入されると、デベロッパーは widthheight を省略し、代わりに CSS を使用して画像のサイズを変更するようになりました。

img {
  width: 100%; /* or max-width: 100%; */
  height: auto;
}

ただし、画像サイズが指定されていないため、ブラウザが画像のダウンロードを開始してサイズを決定できるようになるまで、領域は割り当てられません。画像が読み込まれると、テキストがページ下に移動してスペースが確保されるため、ユーザーが混乱し、不満を感じます。

ここで重要なのがアスペクト比です。画像のアスペクト比は、画像の幅と高さの比率です。2 つの数値をコロンで区切って表するのが一般的です(例: 16:9、4:3)。アスペクト比が x:y の場合、画像は x 単位の幅、y 単位の高さになります。

つまり、一方のディメンションがわかればもう一方も特定できます。アスペクト比が 16:9 の場合:

  • puppy.jpg の高さが 360 ピクセルの場合、幅は 360 x (16 / 9) = 640 ピクセルです。
  • puppy.jpg の幅が 640 ピクセルの場合、高さは 640 x (9 / 16) = 360 ピクセルです。

画像のアスペクト比を把握することで、ブラウザは高さと関連領域に十分なスペースを計算して予約できます。

画像サイズの設定に関する最新のベスト プラクティス

最新のブラウザでは、画像の width 属性と height 属性に基づいて画像のデフォルトのアスペクト比が設定されているため、これらの属性を画像に設定し、上記の CSS をスタイルシートに含めることで、レイアウトの移動を防ぐことができます。

<!-- set a 640:360 i.e a 16:9 aspect ratio -->
<img src="puppy.jpg" width="640" height="360" alt="Puppy with balloons">

これにより、すべてのブラウザで、要素の既存の width 属性と height 属性に基づいてデフォルトのアスペクト比が追加されます。

これにより、画像が読み込まれる前に width 属性と height 属性に基づいてアスペクト比が計算されます。この情報はレイアウト計算の最初に提供されます。画像に特定の幅(width: 100% など)を指定するとすぐに、アスペクト比を使用して高さが計算されます。

この aspect-ratio 値は、デフォルトのユーザー エージェント スタイルシート(理由について詳しくはこちらの投稿を参照)ではなく、HTML の処理時に主要なブラウザで計算されるため、値の表示は若干異なります。たとえば、Chrome では [要素] パネルの [スタイル] セクションに次のように表示されます。

img[Attributes Style] {
  aspect-ratio: auto 640 / 360;
}

Safari も同様に、HTML 属性スタイルソースを使用します。Firefox では、この計算された aspect-ratio は [Inspector] パネルにはまったく表示されず、レイアウトには使用されます。

画像のダウンロード後に画像のサイズによってデフォルトのアスペクト比がオーバーライドされるため、上記のコードの auto の部分は重要です。画像のサイズが異なる場合、画像の読み込み後にレイアウトが一部移動しますが、HTML が正しくない場合でも、画像が利用可能になったときには画像のアスペクト比が維持されます。実際のアスペクト比がデフォルトと異なる場合でも、ディメンションが指定されていない画像のデフォルト サイズ 0x0 よりもレイアウト シフトが少なくなります。

レスポンシブな画像についてさらに詳しく検討し、アスペクト比を詳しく見ていくには、メディアのアスペクト比でジャンクのないページの読み込みをご覧ください。

画像がコンテナ内にある場合は、CSS を使用して画像をコンテナの幅にサイズ変更できます。画像の高さに固定値が使用されないように、height: auto; を設定します。

img {
  height: auto;
  width: 100%;
}

レスポンシブ画像についてはどうですか?

レスポンシブ画像を使用する場合、srcset は、ブラウザで選択できる画像と、各画像のサイズを定義します。<img> の幅と高さの属性を設定できるようにするには、各画像で同じアスペクト比を使用する必要があります。

<img
  width="1000"
  height="1000"
  src="puppy-1000.jpg"
  srcset="puppy-1000.jpg 1000w, puppy-2000.jpg 2000w, puppy-3000.jpg 3000w"
  alt="Puppy with balloons"
/>

画像のアスペクト比も、アート ディレクションに応じて変わる可能性があります。たとえば、狭いビューポート用に切り抜かれた画像を追加し、パソコンでは画像全体を表示できます。

<picture>
  <source media="(max-width: 799px)" srcset="puppy-480w-cropped.jpg" />
  <source media="(min-width: 800px)" srcset="puppy-800w.jpg" />
  <img src="puppy-800w.jpg" alt="Puppy with balloons" />
</picture>

Chrome、Firefox、Safari で、特定の <picture> 要素内の <source> 要素に widthheight を設定できるようになりました。

<picture>
  <source media="(max-width: 799px)" srcset="puppy-480w-cropped.jpg" width="480" height="400" />
  <source media="(min-width: 800px)" srcset="puppy-800w.jpg" width="800" height="400" />
  <img src="puppy-800w.jpg" alt="Puppy with balloons" width="800" height="400" />
</picture>

広告、埋め込み、その他読み込みが遅いコンテンツ

レイアウト シフトを引き起こす可能性のあるコンテンツは、画像だけではありません。広告、埋め込み、iframe、その他の動的に挿入されたコンテンツによって、その後に表示されるコンテンツがシフトダウンして、CLS が増加する可能性があります。

広告は、ウェブ上でのレイアウト移動に最も寄与している要因の一つです。多くの場合、広告ネットワークやパブリッシャーは動的広告サイズをサポートしています。広告サイズによってクリック率が高くなり、オークションで競合する広告が増えるため、パフォーマンスや収益が向上します。しかし、この場合、広告によって閲覧中のコンテンツがページ下部に押しやられてしまうため、ユーザー エクスペリエンスは最適とはなりません。

埋め込み型ウィジェットを使用すると、YouTube の動画、Google マップの地図、ソーシャル メディアの投稿など、ポータブルなウェブ コンテンツをページに組み込むことができます。しかし多くの場合、このようなウィジェットは読み込み前にコンテンツのサイズを認識しません。そのため、埋め込みを提供するプラットフォームでは、ウィジェット用のスペースが常に確保されているとは限らず、最終的に読み込まれるときにレイアウトがずれます。

対処方法はどれもほぼ同じです。主な違いは、挿入されるコンテンツをどの程度制御できるかです。広告パートナーなどの第三者によって挿入される場合、挿入されるコンテンツの正確なサイズを把握できず、それらの埋め込み内で発生するレイアウト シフトを制御できない可能性があります。

遅延読み込みコンテンツ用のスペースを確保する

遅延読み込みコンテンツをコンテンツ フローに配置する場合は、初期レイアウトでコンテンツ用のスペースを確保することで、レイアウト シフトを回避できます。

1 つの方法として、min-height CSS ルールを追加してスペースを確保するか(広告などのレスポンシブ コンテンツの場合は、ブラウザが指定されたディメンションの画像に対してこれを自動的に使用するのと同様の方法で aspect-ratio CSS プロパティを使用する)という方法があります。

1 台目のデバイスではテキスト コンテンツのみを含む 3 つのモバイル デバイス。2 台目のデバイスではシフトダウンされ、3 台目のデバイスに示すようにプレースホルダでスペースを予約することでシフトを防止できます。
広告用のスペースを確保することで、レイアウトの移動を防ぐことができる

場合によっては、メディアクエリを使用して、フォーム ファクタ間で広告やプレースホルダのサイズの微妙な違いを考慮する必要があります。

広告など、高さが固定されないコンテンツでは、レイアウトの移動を完全になくすために必要な十分なスペースを確保できないことがあります。より小さい広告が配信される場合、パブリッシャーはレイアウトの移動を避けるために大きいコンテナのスタイルを設定するか、過去のデータに基づいて最も適切な広告スロットのサイズを選択できます。この方法の欠点は、ページ上の空白が増えることです。

代わりに、初期サイズを使用される最小サイズに設定し、大きなコンテンツの場合はある程度のシフトを許容できます。前述のように min-height を使用すると、親要素を必要に応じて拡大でき、空の要素のデフォルト サイズ 0px と比較してレイアウト シフトの影響を軽減できます。

広告が返されない場合などは、プレースホルダを表示することで予約済みのスペースが折りたたまれないようにしてください。要素用に確保されたスペースを削除すると、コンテンツの挿入と同じ CLS が発生する可能性があります。

読み込みが遅いコンテンツをビューポートの下方に配置する

動的に挿入されたコンテンツがビューポートの上部に近いほど、通常はビューポートの下部に挿入されたコンテンツよりもレイアウトのずれが大きくなります。ただし、ビューポートの任意の場所にコンテンツを注入すると、やはり若干のずれが発生します。挿入されたコンテンツ用のスペースを確保できない場合は、CLS への影響を軽減するためにページの後半に配置することをおすすめします。

ユーザーの操作なしで新しいコンテンツを挿入しない

サイトを読み込もうとしたときに、ビューポートの上部または下部に UI がポップインするため、レイアウトが移動したことを経験したことがあるのではないでしょうか。広告と同様に、ページの他のコンテンツを移動するバナーやフォームでも、このようなことがよく起こります。

スペースが予約されていない動的コンテンツ。

この種の UI アフォーダンスを表示する必要がある場合は、ビューポートに十分なスペースをあらかじめ確保しておきます(プレースホルダ UI やスケルトン UI を使用するなど)。それにより、ページの読み込み時にページのコンテンツが予期せず移動しないようにします。または、該当する要素がドキュメント フローの一部にならないように、コンテンツをオーバーレイします。このようなコンポーネントに関するその他の推奨事項については、Cookie に関する通知に関するおすすめの方法の投稿をご覧ください。

場合によっては、コンテンツを動的に追加することが、ユーザー エクスペリエンスの重要な部分を占めることがあります。たとえば、商品アイテムのリストに追加の商品を読み込む場合や、公開中のフィード コンテンツを更新する場合などです。そのような場合に予期しないレイアウト シフトを回避する方法はいくつかあります。

  • 固定サイズのコンテナ内の古いコンテンツを新しいコンテンツに置き換えるか、カルーセルを使用して移行後に古いコンテンツを削除します。新しいコンテンツを取り込む際に誤ってクリックしたりタップしたりしないように、移行が完了するまではリンクやコントロールを無効にしてください。
  • ユーザーに新しいコンテンツの読み込みを開始してもらいます。これにより、移動してもユーザーが驚かないようにすることができます([さらに読み込む] ボタンや [更新] ボタンが表示されるなど)。ユーザー操作の前にコンテンツをプリフェッチして、すぐに表示されるようにすることをおすすめします。なお、ユーザー入力から 500 ミリ秒以内に発生するレイアウト シフトは、CLS にカウントされません。
  • コンテンツをオフスクリーンにシームレスに読み込み、利用可能であることをユーザーに通知します(「上にスクロール」ボタンなど)。
Twitter と Chloé のウェブサイトから予期しないレイアウト シフトを生じない動的コンテンツの読み込みの例
予期しないレイアウト シフトを発生させずに動的コンテンツを読み込む例左: Twitter でライブフィードのコンテンツを読み込んでいます。右: Chloé ウェブサイトの「Load More」の例。YNAP チームがより多くのコンテンツを読み込むときに CLS に合わせて最適化した方法をご確認ください。

アニメーション

CSS プロパティ値を変更すると、ブラウザはその変更に対応する必要があります。box-shadowbox-sizing などの一部の値は、再レイアウト、ペイント、コンポジットをトリガーします。top プロパティと left プロパティを変更すると、移動する要素がそれ自体のレイヤにある場合でも、レイアウト シフトが発生します。これらのプロパティを使用してアニメーション化しないでください。

他の CSS プロパティは、再レイアウトをトリガーせずに変更できます。たとえば、transform アニメーションを使用した要素の平行移動、スケーリング、回転、偏りなどです。

translate を使用して合成したアニメーションは、他の要素に影響しないため、CLS にはカウントされません。また、合成されていないアニメーションでも再レイアウトは発生しません。レイアウト シフトをトリガーする CSS プロパティについて詳しくは、高パフォーマンスのアニメーションをご覧ください。

ウェブフォント

通常、ウェブフォントのダウンロードとレンダリングは、ダウンロード前に次の 2 つの方法のいずれかで処理されます。

  • 代替フォントがウェブフォントとスワップされ、スタイルなしテキストの Flash(FOUT)が発生します。
  • 「非表示」のテキストは、ウェブフォントが利用可能になり、テキストが表示されるようになるまで、代替フォントを使用して表示されます(FOIT: 非表示テキストのフラッシュ)。

どちらの方法でもレイアウト シフトが発生する可能性があります。テキストは非表示の場合でも代替フォントを使用してレイアウトされるため、ウェブフォントが読み込まれると、テキスト ブロックと周囲のコンテンツが、表示フォントと同様にシフトします。

次のツールを使用すると、テキストのシフトを最小限に抑えることができます。

  • font-display: optional は再レイアウトを回避できます。ウェブフォントは初期レイアウト時までに使用可能な場合にのみ使用されるためです。
  • 適切な代替フォントが使用されていることを確認します。たとえば、font-family: "Google Sans", sans-serif; を使用すると、"Google Sans" の読み込み中にブラウザの代替フォント sans-serif が確実に使用されます。font-family: "Google Sans" のみを使用して代替フォントを指定しない場合、デフォルトのフォントが使用されます。Chrome では、この Serif フォントはデフォルトの sans-serif フォントよりも適した Serif フォントです。
  • フォント フォールバックの改善に関する投稿で詳しく説明されているとおり、新しい size-adjust API、ascent-override API、descent-override API、line-gap-override API を使用して、代替フォントとウェブフォントのサイズの違いを最小限に抑えます。
  • Font Loading API を使用すると、必要なフォントの取得にかかる時間を短縮できます。
  • <link rel=preload> を使用して、重要なウェブフォントをできるだけ早く読み込みます。プリロードされたフォントは、First Paint に対応する可能性が高くなり、この場合、レイアウト シフトは発生しません。

フォントに関するその他のベスト プラクティスについては、フォントに関するおすすめの方法をご覧ください。

ページが bfcache の対象であることを確認して CLS を削減する

CLS スコアを低く抑えるには、ウェブページをバックフォワード キャッシュ(bfcache)の対象にすることが非常に効果的です。

bfcache は、ページから移動した後も少しの間、ブラウザのメモリにページを保持します。そのため、そのページに戻った場合、元のページが完全に復元されます。つまり、読み込まれたページはすぐに利用可能になります。前述の理由によって、読み込み時に通常見られるようなシフトはありません。

それでも、最初のページ読み込みでレイアウト シフトが発生する可能性はありますが、ユーザーがページに戻ったときに同じレイアウト シフトが繰り返し表示されることはありません。初期読み込みでも必ずシフトを避けるようにする必要がありますが、完全に解決するのが難しい場合は、少なくとも bfcache のナビゲーションでシフトを回避することで影響を軽減できます。

「戻る」と「進む」のナビゲーションは多くのサイトでよく使用されています。たとえば、目次ページ、カテゴリページ、検索結果などに戻ります。

これが Chrome に展開されると、CLS には顕著な改善が見られました

bfcache はすべてのブラウザでデフォルトで使用されますが、サイトによってはさまざまな理由により bfcache に対応していないことがあります。bfcache ガイドで bfcache の使用を妨げる問題をテストして特定する方法を確認し、この機能を最大限に活用してサイトの全体的な CLS スコアを改善してください。

おわりに

このガイドの前半で詳しく説明したように、CLS を特定して改善するためのさまざまな手法があります。Core Web Vitals には割引が組み込まれているため、CLS を完全に排除できない場合でも、いくつかの手法を使用することで影響を軽減できるはずです。この制限内に収め、ウェブサイトのユーザー エクスペリエンスを向上させることが目的です。