記事を読んでいたら文章が突然下に飛ぶ。ボタンを押そうとしたら別のリンクに差し変わって誤タップする。この不快感を数値化したのが CLS(Cumulative Layout Shift) です。
アイデアマンズでは通販サイトを中心にサイトスピード改善を手がけています。その現場で、CLSを大きく押し上げる原因は繰り返し2つのパターンに集約されます。画像への width/height 未指定と、カルーセルの初期高さ未確保です。どちらもHTMLとCSSの数行で対処できます。
この記事では、その仕組みとコードを整理し実在4サイトでの改善数値を紹介します。
CLSはLCPとは別軸の指標
CLSは、ページ読み込み中に発生したレイアウトのずれを累積したCore Web Vitalsの指標です。Googleは0.1以下を「良好」、0.25以上を「改善が必要」と定義しています。
LCPやFCPが「コンテンツが表示されるまでの時間」を測るのに対し、CLSは「読み込み中にレイアウトがどれだけずれたか」を測ります。表示が速くてもページがガタガタ動けばCLSは高くなります。両者は独立した軸です。
もっともCLSは対処しやすく、丁寧にHTMLとCSSを書けば防げる確率が高い指標です。Core Web VitalsがSEOで注目され始めた2021〜2022年ごろには、すでに対策を終えていたサイトも多くありました。
なぜ画像でCLSが起きるのか
<img> に width と height がないと、ブラウザは画像の表示サイズを事前に知れません。HTML解析の時点では高さがゼロとして扱われ、ダウンロード完了の瞬間に実サイズで描画されます。この切り替わりが周囲のテキストやボタンを一気に押しのけます。
処理の流れを図にすると、属性の有無で経路が二手に分かれます。
width/height を指定すると、ブラウザはアスペクト比から表示領域を計算し、ダウンロード完了前から空間を確保します。これだけでレイアウトシフトは大幅に抑えられます。
<!-- 高さを事前計算できず、画像読み込み完了時にずれが発生する -->
<img src="banner.jpg" alt="キャンペーンバナー">
<!-- width/heightでアスペクト比から空間が事前確保される -->
<img src="banner.jpg" alt="キャンペーンバナー" width="800" height="400">
レスポンシブ対応で固定ピクセルを指定しにくい場合は、CSSの aspect-ratio でも同じ効果が得られます。
/* aspect-ratioでアスペクト比を事前指定する代替手段 */
.logo-image {
aspect-ratio: 200 / 80;
width: 100%;
height: auto;
}
なお width/height 属性を指定しつつ、CSSで width: 100%; height: auto; を併用しても問題ありません。ブラウザは属性からアスペクト比を計算して空間を確保し、CSSで実際の表示サイズへスケールさせます。
カルーセルでCLSが起きる構造
カルーセル(スライダー)はJavaScriptで初期化されます。HTML読み込み直後は全スライドが縦に積み重なって表示されることが多く、1枚表示へ切り替わった瞬間にページの高さが急変します。
根本の原因は、JavaScript初期化前の高さが未定義のまま放置されている点です。初期状態の高さをCSSで制限しておけば、初期化後の高さ変化を最小限に抑えられます。
/* JS初期化前に1枚目だけ表示してシフトを抑える例(slick.jsの場合) */
.main-slider:not(.slick-initialized) .slide {
display: none;
}
.main-slider:not(.slick-initialized) .slide:first-child {
display: block;
}
ライブラリによって実装は異なりますが、「JS初期化前に高さが未定義のコンテナができる」構造は共通しています。コンテナに最小高さをCSSで与える方法も有効です。
実在4サイトでの改善数値
弊社の表示速度ボトルネック実例研究で、これらの問題を4サイトで観測しました。各数値はサードパーティタグの影響を除いた、サイト固有のシミュレーション結果です。タグを含む観測値とは若干異なる場合があります。
タマチャンショップ では、lazyloadされる画像の src が空で width/height も未指定でした。加えてFlickityスライダーの高さも未確保でした。複合要因が重なり、CLSは0.447でした。SVGプレースホルダーと正確な属性の付与、スライダーへのCSS初期高さの確保でCLSは0.047まで下がりました。89%の変化です。
富澤商店(TOMIZ) では、ページ内131個の <img> のうち92個に width/height がありませんでした。実寸に基づく属性を追加しただけで、CLSは0.323から0.061へ改善しました。総合スコアも82から98へ上がっています。
Billboard Japan では、74箇所すべての <img> に属性がありませんでした。CLSは0.721という極端な値でした。全箇所への属性追加と img { max-width: 100%; height: auto; } の併用で、CLSは0.002まで下がりました。副次的にLCPも2.1秒から1.4秒へ短縮しています。
<!-- 変更前: ブラウザが高さを確保できない -->
<img src="/common/sys/img/specialbanner/1634/image_l_1.jpg" alt="">
<!-- 変更後: アスペクト比から空間が事前確保される -->
<img src="/common/sys/img/specialbanner/1634/image_l_1.jpg" alt="" width="1327" height="886">
ABAHOUSE ONLINE STORE では、slick.jsのカルーセルと画像属性の未指定が重なっていました。JS初期化前に全15スライドが縦展開され、CLSは0.527でした。カルーセルの初期高さ制限でCLSは0.107まで下がり、残る画像属性の追加で0.037に達しています。カルーセルのシフトが支配的で、画像属性はその残差を取り除いた形でした。
自分のサイトで対処する手順
まずChrome DevToolsの「Performance」タブでページを読み込みます。「Experience」セクションにLayout Shiftのイベントが記録され、どの要素がシフトを引き起こしているかを確認できます。Google Search ConsoleのCore Web Vitalsレポートも、実ユーザーのCLS傾向の把握に役立ちます。
原因が <img> なら、その要素に width/height を追加します。CMSやECプラットフォームが自動生成するHTMLなら、テンプレートを修正するか、属性を出力させる設定を確認します。
原因がカルーセルなら、初期状態の高さをCSSで制限します。ライブラリが初期化後に付与するクラスで制限を解除する方法と、コンテナに最小高さを与える方法のどちらかを取ります。ライブラリのバージョンアップでクラス名や初期化タイミングが変わることもあるため、修正後はDevToolsでシフトの解消を確認します。
まとめ
- CLSはLCPやFCPとは別軸の指標で、測るのはレイアウトの安定性
- 画像の
width/height未指定は最も多い原因のひとつ。富澤商店で92個、Billboard Japanで74個の属性追加が16〜24ポイントの総合スコア改善に直結 - カルーセルはJS初期化前の高さ確保が要点。ABAHOUSEではCLS 0.527の主因がslick.jsの初期化前後のシフト
- 対処の難易度は低く、コードの変更量は少なめでサーバー設定も不要
各サイトの詳しい計測条件は、次の記事にまとめています。


