CLS(Cumulative Layout Shift)
とは何か
CLS(Cumulative Layout Shift)
とは、ページのライフスパン全体において発生する予期せぬレイアウトシフト
ごとの、レイアウトシフトスコア
の最大バーストの計測値、とのこと。(なるほど、わからん)
(レイアウトシフトがどんな現象かについては、この記事で紹介されてる動画1がわかりやすい)
レイアウトシフト
(後述)の発生は、すくなくともユーザへ不快感を抱かせる要因になりうるばかりか、ECなどの業種によってはカスタマートラブル等のビジネス上の大きなトラブルにすら発展しうるリスクを孕んだUI上の問題だったりする。
そしてもちろん、レイアウトシフト
はパフォーマンス低下要因そのものでもあるため、離脱率やコンバージョンの悪化にも直結する。
(技術的にもビジネス的にもリスクが過小評価されているorそもそも認識すらされてないのかもしれない。)
これらを発生させる原因として、画像などのリソースが非同期に(というか画像の読み込みは大体非同期だが)読み込まれたり、既存のコンテンツの上部に動的にDOM要素が追加されたり、などがあり、これらが原因となって「コンテンツの位置が不意にずれること」をレイアウトシフト(Layout shift)
という。
レイアウトシフト
を起こす犯人としては、(画面上の)大きさがロードするまで分からない画像や動画、デフォルトフォントと(同じフォントサイズでも描画上の)大きさが異なるフォント、あるいはサードパーティの広告やウィジェットなどが挙げられる。
そしてCLS(Cumulative Layout Shift)
は、このレイアウトシフト
がページ内でどれだけ起きているかを計測している指標である。
CLS
の目標スコアとしては、0.1以下が望ましいとされる。
また、このタイムをほとんどのユーザで体験できることを目指すのであれば、ページロードにおいて75パーセンタイルに当たるところが良い閾値となるとのこと。
もっと詳しい話
なんでレイアウトシフト
すぐ起こしてしまうん?
web.devのCLS
解説記事では、以下のように説明している。
What makes this issue even more problematic is that how a site functions in development is often quite different from how users experience it. Personalized or third-party content often doesn't behave the same in development as it does in production, test images are often already in the developer's browser cache, and API calls that run locally are often so fast that the delay isn't noticeable.
この問題を困難にしている要因は、サイトの機能がしばしば開発時とユーザが体験するものとで大きな差異があることである。パーソナライズされたもしくはサードパーティのコンテンツはしばしば開発時と本番とで同じではないし、テスト画像は大体既に開発者のブラウザのキャッシュ上にあり、そしてローカルで動作するAPIの呼び出しは目立った遅延もなくとても高速だ。
つまり、開発時点でレイアウトシフト
の大きさやタイミングについて正確に(計測・)把握しないまま本番に出てしまい、それによって「こんなはずじゃなかった」悪いユーザ体験をもたらしてしまっている、と言える。
セッションウィンドウ(session window)
という概念
レイアウトシフト
は、可視要素がその描画されたレンダリングフレームから位置を変更されればいつでも発生する。
そのためCLS
では、session window
というのをつかってレイアウトシフト
を計測している。
session window
とは、連続して発生するデータをある短い時間ごとに塊として考える手法で、データが発生し始めてから(最大時間を超えない限りで)その連続発生が収まるまでの間を1つの「ウィンドウ」としてグループ化し、そのウィンドウごとに処理や分析をする手法やそのウィンドウ自体のこと。
CLS
では、1つ以上の個別のレイアウトシフト
が各シフトの間に1秒未満、合計ウィンドウ期間で最大5秒の間ですばやく連続して発生した時に「レイアウトがバーストした」とみなし、これをsession window
として扱う。
そしてCLS
の計測対象である「(レイアウトシフト
の)最大バースト」とは、ウィンドウ内のすべてのレイアウトシフト
を累積したスコアが最大なsession window
を指す。つまりこの最大値を計測したsession window
のレイアウトシフトスコア
がCLS
の計測値になる。
レイアウトシフトスコア(Layout shift score)
について
レイアウトシフトスコア(Layout shift score)
とは、変化を起こした要素が「どれだけの画面内で変化のインパクトを発生させたか (impact fraction
)」と「どれだけの画面内を移動したか (distance fraction
)」の積を取る。
layout shift score = impact fraction * distance fraction
impact fraction
について
impact fraction
とは前述の通り、ある2つの描画フレーム間で画面範囲(ビューポート)領域内に与えたインパクトを計測したもの。
具体的には、全ての不安定(unstable; レイアウトシフトの原因)な要素の、現在の描画フレームからみた前回の描画フレームからの可視領域の和集合のビューポート全域にしめる割合(分数:fraction)が現在のフレームのimpact fraction
に当たる。
例えば、1フレーム前に画面の上から半分(50%)を占めていた要素が、現在のフレームでは上から25%の位置まで下がってきたとすると、そのとき要素の両フレーム間での和集合(両方の画面を半透明にしてピッタリ重ねたときようなイメージ。全くカブってなかったらただの面積の和)は画面全体75%(そのうち50%がカブってる)になるので、そのとき(現在フレームの)impact fraction
は0.75
となる。
1つ前のフレーム
┏━━━━━━━━━━━━━━━━━┓
┃ ┌─────────────┐ ┃
┃ │ │ ┃
┃ │ unstable │ ┃
┃ │ │ ┃
┃ └─────────────┘ ┃
┃ ┃
┃ ┃
┃ ┃
┃ ┃
┗━━━━━━━━━━━━━━━━━┛
現在のフレーム
┏━━━━━━━━━━━━━━━━━┓
┃ ┌-------------┐ ┃
┃ │ │ ┃
┃ ┌─────────────┐ ┃
┃ │ │ ┃
┃ │ unstable │ ┃
┃ │ │ ┃
┃ └─────────────┘ ┃
┃ ┃
┃ ┃
┗━━━━━━━━━━━━━━━━━┛
↑↑の和集合
┏━━━━━━━━━━━━━━━━━┓
┃ ┌─────────────┐ ┃
┃ │ │ ┃
┃ │ union │ ┃
┃ │ of │ ┃
┃ │ unstable │ ┃
┃ │ │ ┃
┃ └─────────────┘ ┃
┃ ┃
┃ ┃
┗━━━━━━━━━━━━━━━━━┛
distance fraction
について
distance fraction
とは前述の通り、ある2つの描画フレーム間でビューポート領域内を移動した距離を計測したもの。
具体的には、いずれかの不安定(unstable; レイアウトシフトの原因)な要素が移動した距離をビューポートが横長なら水平移動距離 / ビューポートの幅
、縦長なら垂直移動距離 / ビューポートの高さ
)で算出される割合(分数:fraction)が現在のフレームのdistance fraction
に当たる。
前出の例で言うと、ビューポートは縦長なので高さを基準に、移動距離は25%なので、このとき(現在フレームの)distance fraction
は0.25
となる。
そして、impact fraction
の0.75
とdistance fraction
の0.25
の積を取って、0.75 * 0.25 = 0.1875
がこのとき(現在フレームの)レイアウトシフトスコア(Layout shift score)
となる。
より詳細な条件での計算例は、web.devのCLS
説明記事の「Layout shift score」の項を参照のこと。
2021年1月以前の古いCLS
実装について
かつてCLS
はページのライフスパン全体における全てのレイアウトシフトの合算値を計測されていたり、その元であるレイアウトシフトスコア
についてもimpact fraction
のみに基づいて計測されたりしていた。
しかし、2021年1月にCLS
の実装が計測方法に変更されたため、それ以前に言及されたCLS
の実装についての説明記事・動画等は内容が正確でない可能性がある。
仕様変更の詳細な理由などについては、web.devのEvolving the CLS metricの記事を参照のこと。
参照
-
キャンセルしようと「いいえ、戻る」を押したつもりが、レイアウトシフトが起きて「はい、注文します」を押してしまい、あばばばばばば!(→注文完了)となってしまうの図 ↩