skip to content

How is the score for a single layout shift calculated, and how are the individual shifts on a page combined into the reported Cumulative Layout Shift (CLS) value?

level: middleimportance: must knowfreq 56%

answer

  1. two fractions, multiplied together
  2. area union across both frames
  3. distance over the viewport's larger side
  4. bursts, not a lifetime total
  5. worst window wins

basics

~20 s

Each shift scores impact fraction times distance fraction: the share of the viewport the moving content covered before and after, times how far it moved relative to the viewport's larger side. The page reports the largest sum inside one session window.

solid answer

~50 s

A single layout shift's score is the **impact fraction** multiplied by the **distance fraction**. The impact fraction is the union of the unstable element's visible area in the previous frame and in the current frame, as a share of the viewport. The distance fraction is the greatest distance any unstable element moved, divided by the viewport's larger dimension. So an element covering half the viewport that drops by a quarter of the viewport height scores 0.75 × 0.25 = 0.1875. Individual scores are then grouped into **session windows**: a run of shifts where consecutive shifts are less than one second apart, capped at five seconds total. CLS is the sum of the largest such window, not the sum of everything. That change, adopted in 2021, stopped the metric from punishing long-lived pages simply for staying open.

code

javascript · 7 lines
javascript
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (entry.hadRecentInput) continue;
    console.log('shift score', entry.value.toFixed(4), 'at', Math.round(entry.startTime), 'ms');
  }
});
observer.observe({ type: 'layout-shift', buffered: true });

go deeper

for a junior

Know that a shift's score comes from how much of the screen moved and how far, and that the two are multiplied — you are not expected to compute it, but you should not describe CLS as a time.

for a middle

Be able to walk the arithmetic out loud on the canonical example: a half-viewport element pushed down a quarter of the viewport gives 0.75 times 0.25, or 0.1875. Explain why the impact fraction unions the before and after areas.

for a senior

Explain session windows and why the lifetime sum was abandoned, then use it: know that one severe moment and a repeated severe moment score alike, so the headline number needs per-shift logging behind it before you can prioritise fixes.

for a principal

Own the consequence for how the organisation reports: a single metric that captures peak severity but not frequency will under-report a chronically jumpy product. Decide what supplementary signal — shift counts, per-route attribution — your monitoring keeps alongside it.

## Two fractions, multiplied A layout shift's score is deliberately built from two viewport-relative ratios so it stays comparable across screen sizes: **layout shift score = impact fraction × distance fraction** ### Impact fraction — how much of the screen was involved Take every unstable element in this frame — elements that were visible in the previous frame and start somewhere else now. Union the area they occupied in the *previous* frame with the area they occupy in the *current* frame, clipped to the viewport, and divide by the viewport area. The union is the key detail: a block that occupies the top half of the screen and moves down a quarter of the screen now touches 75% of the viewport across the two frames, not 50%. ### Distance fraction — how far it travelled Take the greatest distance any single unstable element moved, horizontally or vertically, and divide it by the viewport's **larger** dimension (width or height, whichever is bigger). This is capped by construction at 1. ### Worked example A text block fills the top half of a phone viewport. A banner loads and pushes it down by 25% of the viewport height. - Impact fraction: it covered 50% before, 50% after, overlapping — the union is 75% → **0.75** - Distance fraction: it moved 25% of the viewport height → **0.25** - Score: 0.75 × 0.25 = **0.1875** One such shift already exceeds the widely used 0.1 "good" guidance. That is the intuition to carry away: a *single* full-width push of visible content is enough to fail the metric, so the goal is zero unexpected shifts, not "few". ### Why a product, not a sum Both factors matter and neither alone describes the harm. Content moving one pixel across the whole screen is not damaging; a tiny off-to-the-side element jumping a long way is not either. The pain is *lots of content* moving *far*, and multiplication is what expresses "both, at once". ## From individual shifts to CLS: session windows Each shift's score is not simply added to a running total for the page. Shifts are grouped into **session windows**: - a window opens with the first shift; - it keeps absorbing shifts as long as each new shift is **less than one second** after the previous one; - it closes after **five seconds** at most, however busy the page is. CLS is the score of the **largest** window — the worst single burst of instability on the page. ### Why the definition changed The original metric was a plain sum over the page's lifetime. That produced a perverse result: two pages could be equally jumpy per interaction, and the one users *liked enough to stay on* would score worse, because more time on the page meant more shifts added. Long-lived single-page apps, infinite feeds, and dashboards were penalised for engagement. The session-window definition, adopted in 2021, fixed that by asking "how bad was the worst moment?" instead of "how much did it add up to?". A practical consequence: a page with one truly awful moment scores exactly as badly as a page that repeats that moment every thirty seconds. If you want the frequency of shifts as well as their peak severity, you have to log the individual shift entries yourself — the headline number will not tell you. ## Reading the numbers yourself The per-shift score is exposed directly, so you never have to guess at the arithmetic: ```js new PerformanceObserver((list) => { for (const entry of list.getEntries()) { if (entry.hadRecentInput) continue; console.log(entry.value.toFixed(4), entry.startTime, entry.sources); } }).observe({ type: 'layout-shift', buffered: true }); ``` `entry.value` is that single shift's score, `entry.startTime` lets you see whether shifts cluster into one window or are spread out, and `entry.sources` names the nodes with their previous and current rectangles, which is what turns a number into a fix. ## What the model tells you about fixing things Because distance is measured against the *viewport's* larger dimension and impact against the *viewport's* area, the same code produces very different scores on a phone and a desktop — a 60 px banner is a much larger fraction of a small screen. That is one reason mobile field data is usually the worse of the two, and why you should reproduce with a mobile viewport rather than a desktop window. And because the impact fraction uses the union across two frames, shifting content *upwards* is exactly as expensive as shifting it down. There is no cheap direction.

  • Why does the impact fraction use the union of the before and after areas rather than just the element's area?
    Because the harm is the region of the screen the user saw change, and that region spans both positions. An element that moves clear of its old position disturbs twice its own area; one that barely nudges disturbs only slightly more than its area. The union captures that difference naturally, and it means a large move is penalised through both factors.
  • Two pages each have one shift scoring 0.15, but one page repeats that shift every thirty seconds. How do their CLS values compare?
    They are identical: 0.15. Session windows are capped at five seconds and a gap of a second closes the window, so shifts thirty seconds apart never combine — CLS reports the worst window, not the count. If you care about how often instability happens, you have to log individual `layout-shift` entries and count them yourself.
  • Why is the distance fraction divided by the viewport's larger dimension rather than its height?
    It normalises the score the same way for vertical and horizontal movement and for both orientations, so a sideways jolt on a wide screen is not scored on a different scale from a vertical one. It also bounds the fraction at 1, keeping any single shift's score at or below the impact fraction.

saying these in an interview costs you the question

  • Says CLS is the plain sum of every shift on the page
  • Uses the element's own area instead of the before/after union
  • Divides the distance by the element's size, not the viewport
  • Thinks the score has pixel or millisecond units
  • Believes a single shift can never breach the guidance

context