レイアウトシフト(CLS)の正体:ブラウザはなぜ「ガタつき」を許してしまうのか
フロントエンド開発の現場で、「CLS(Cumulative Layout Shift)が改善しない」と頭を抱えたことはないだろうか。ユーザーが記事を読もうとした瞬間に広告が割り込み、クリックしたリンクが指の下から逃げていく――。あのUXの悪夢は、実はブラウザのレンダリングパイプラインの「律儀さ」が引き起こしている悲劇なんだ。
今日は、ブラウザが裏側でどうやって画面を描画し、なぜ君の書いたコードが予期せぬ「ガタつき」を生むのか、そのメカニズムを深掘りしていこう。
—
ブラウザのレンダリングパイプライン:静寂から混沌へ
ブラウザがHTMLを読み込んでから画面を表示するまで、裏では以下の過酷なリレーが行われている。
1. DOM/CSSOM構築: HTML/CSSを解析してツリーを作る。
2. Render Tree構築: DOMとCSSOMをマージして、「画面に表示すべき要素」のツリーを作る。
3. Layout (Reflow): 各要素の「幾何学的な位置とサイズ」を計算する。
4. Paint: ピクセルを塗りつぶす。
5. Composite: レイヤーを合成して画面に焼き付ける。
ここで重要なのは、「Layout」はコストが非常に高い処理だということだ。画面上のひとつの要素のサイズが変われば、それに依存する周囲の要素の座標をすべて再計算しなければならない。ブラウザは、後から追加された要素のために、この計算をやり直す「リフロー」という重い作業を強いられる。これがCLSの物理的な原因だよ。
—
なぜ「画像サイズの未指定」が致命的なのか
ブラウザは、HTMLを上から順にパースしていく。このとき、``タグに`width`と`height`が指定されていないと、ブラウザは「画像が読み込まれるまで、そのサイズがわからない」という状態に陥る。
ブラウザは賢いから、とりあえず`0x0`の領域を確保して読み込みを待つ。ところが、画像がダウンロードされた瞬間に「あ、本当は横1000pxあったわ」と判明する。するとブラウザは、「やべっ、レイアウト計算し直さなきゃ!」と慌てて再計算を始め、すでに表示されていた下のコンテンツをグイッと押し下げるわけだ。
—
実践:CLSを封じ込める「アスペクト比固定」の鉄則
今、現場で最も推奨されているのは、ブラウザに対して「読み込み前から領域を予約させる」ことだ。CSSの `aspect-ratio` プロパティを使えば、画像のサイズが不明でも、あらかじめ適切なスペースを確保できる。
以下に、モダンなフロントエンド開発で即戦力となる実装例を挙げるね。
このコードのポイントは `aspect-ratio` と、HTMLの `width`/`height` 属性の併用だ。HTML属性があれば、CSSが読み込まれる前のわずかな瞬間でさえ、ブラウザは正しく領域を計算できる。これが「防衛的コーディング」の極意だよ。
—
動的挿入の罠:JavaScriptで要素を足すとき
広告やSNSの埋め込みをJavaScriptで動的に挿入する場合も、同じことが起きる。要素を挿入する親コンテナに「高さ」が定義されていないと、コンテンツが表示された瞬間にレイアウトが崩壊する。
解決策は明確だ:
- プレースホルダーを作る: 挿入される予定の要素の最小サイズをCSSで確保しておく。
- `min-height` の活用: コンテンツが空のときでも、最低限の高さを持たせる。
/ 広告枠のプレースホルダー /
.ad-slot {
min-height: 250px; / 広告が読み込まれる前に領域を確保 /
width: 100%;
background: #fafafa;
}
—
最後に:シニアエンジニアからのアドバイス
Webブラウザは優秀なエンジンだけど、魔法使いじゃない。君が「何を、どれくらいのサイズで表示するか」を明確に指示してやらないと、ブラウザは何度も何度も計算をやり直す羽目になる。
CLSを減らすことは、単なるスコア稼ぎじゃない。「ユーザーが今まさに読もうとしている場所を、ブラウザが勝手に動かさないようにする」という、ユーザーへの礼儀なんだ。
次にコードを書くときは、ブラウザが「あ、ここには後でこれが入るんだな」と予測しやすい設計になっているか、一呼吸おいて確認してみてほしい。その些細な気遣いが、君のプロダクトを「手に馴染む」最高のものにするはずだよ。
さあ、エディタに戻って、まずはレイアウトが暴れている場所を `aspect-ratio` で鎮圧することから始めよう。健闘を祈るよ。

コメント