15 CSS Aspect Ratio Demos08 / 15

CSS + 20-line JSMIT licensed

CSS Aspect Ratio Prevent Layout Shift Placeholder

A split-screen lab that makes Cumulative Layout Shift visible: the same article twice, images arriving on a simulated slow connection. The left pane reserves nothing — headlines lurch as each photo lands. The right pane gives every image aspect-ratio: 3/2 plus a shimmer skeleton — pixels pour into pre-reserved boxes and the text never moves. A live CLS meter (PerformanceObserver) keeps score, and a replay button re-runs the network as many times as you need to believe it.

Published

Live Demo
Try it

The code

<section class="car-08" aria-label="Prevent layout shift placeholder demo">
  <div class="car-08__stage" id="car08-top">
    <header class="car-08__bar">
      <div>
        <h1>Layout-shift lab</h1>
        <p>Same article, same slow network. One pane reserves image space with <code>aspect-ratio</code>; one doesn't.</p>
      </div>
      <p class="car-08__cls" aria-live="polite">CLS recorded <b>0.0000</b><span>Chromium only</span></p>
      <button class="car-08__replay" type="button">↻ Replay slow network</button>
    </header>
    <div class="car-08__split">
      <article class="car-08__pane car-08__pane--raw" aria-label="Without reservation">
        <header class="car-08__panehead"><b class="car-08__dot car-08__dot--bad" aria-hidden="true"></b><h2>Unreserved</h2><span>img { height: auto } — nothing else</span></header>
        <div class="car-08__feed">
          <h3>Trail report: the long way around Sorapiss</h3>
          <p class="car-08__meta">Ren Ito · 11 min read</p>
          <img data-src="https://images.unsplash.com/photo-1464822759023-fed622ff2c3b?q=80&w=1200&h=800&auto=format&fit=crop" width="1200" height="800" alt="Mountain ridge at dawn" class="car-08__img car-08__img--raw">
          <p>The photo above arrives late — and when it does, this paragraph gets shoved down by its full height. On a phone, that's the moment you tap the wrong link.</p>
          <img data-src="https://images.unsplash.com/photo-1501785888041-af3ef285b470?q=80&w=1200&h=800&auto=format&fit=crop" width="1200" height="800" alt="Lake at golden hour" class="car-08__img car-08__img--raw">
          <p class="car-08__marker">⬑ Watch this line jump on every replay.</p>
        </div>
      </article>
      <article class="car-08__pane car-08__pane--safe" aria-label="Reserved with aspect-ratio">
        <header class="car-08__panehead"><b class="car-08__dot car-08__dot--good" aria-hidden="true"></b><h2>Reserved</h2><span>img { aspect-ratio: 3/2 } + shimmer</span></header>
        <div class="car-08__feed">
          <h3>Trail report: the long way around Sorapiss</h3>
          <p class="car-08__meta">Ren Ito · 11 min read</p>
          <img data-src="https://images.unsplash.com/photo-1464822759023-fed622ff2c3b?q=80&w=1200&h=800&auto=format&fit=crop" width="1200" height="800" alt="Mountain ridge at dawn" class="car-08__img car-08__img--safe">
          <p>The box above existed at its final size before a single byte of image arrived. Pixels pour in; geometry holds; this paragraph has never moved.</p>
          <img data-src="https://images.unsplash.com/photo-1501785888041-af3ef285b470?q=80&w=1200&h=800&auto=format&fit=crop" width="1200" height="800" alt="Lake at golden hour" class="car-08__img car-08__img--safe">
          <p class="car-08__marker">⬑ This line has been exactly here since first paint.</p>
        </div>
      </article>
    </div>
    <footer class="car-08__foot">CLS is a Core Web Vitals ranking signal; 0.1 is the whole-page budget and one unreserved hero can spend it. Reserve with <code>aspect-ratio</code> (design-imposed shape) or <code>width</code>/<code>height</code> attributes (per-asset shape). A demo from the CodeFronts aspect-ratio collection.</footer>
  </div>
</section>
.car-08,
.car-08 *,
.car-08 *::before,
.car-08 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.car-08 {
  --bg: oklch(0.15 0.012 260);
  --panel: oklch(0.2 0.015 260);
  --ink: oklch(0.95 0.006 250);
  --mut: oklch(0.66 0.015 255);
  --line: oklch(0.31 0.02 260);
  --good: oklch(0.75 0.17 150);
  --bad: oklch(0.66 0.21 25);
  --acc: oklch(0.78 0.13 220);
  font-family: 'Segoe UI',system-ui,sans-serif;
  color: var(--ink);
  container-type: inline-size;
  display: block;
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  background: var(--bg);
}

.car-08__stage {
  width: 100%;
  container: car08/inline-size;
  height: 100vh;
  height: 100svh;
  overflow-y: auto;
  background: var(--bg);
}

.car-08__bar {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
  padding: 20px clamp(16px,4cqi,40px);
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  z-index: 5;
  background: color-mix(in oklch,var(--bg) 88%,transparent);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
}

.car-08__bar h1 {
  font-size: clamp(17px,2.4cqi,22px);
  letter-spacing: -.02em;
  font-weight: 800;
}

.car-08__bar p {
  font-size: 12.5px;
  color: var(--mut);
  margin-top: 3px;
  max-width: 52ch;
}

.car-08__bar code,
.car-08__foot code,
.car-08__panehead span {
  font-family: ui-monospace,Menlo,Consolas,monospace;
}

.car-08__bar code,
.car-08__foot code {
  font-size: .92em;
  background: oklch(0.26 0.02 260);
  padding: 2px 6px;
  border-radius: 5px;
  color: var(--acc);
}

.car-08__cls {
  margin-left: auto;
  text-align: right;
  font-size: 11px;
  color: var(--mut);
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
}

.car-08__cls b {
  font-family: ui-monospace,Menlo,monospace;
  font-size: 20px;
  color: var(--acc);
  font-weight: 700;
}

.car-08__replay {
  display: grid;
  place-items: center;
  min-height: 46px;
  padding: 0 18px;
  border-radius: 12px;
  border: 1.5px solid var(--acc);
  background: transparent;
  color: var(--acc);
  font: inherit;
  font-size: 13.5px;
  font-weight: 700;
  cursor: pointer;
  transition: background .2s,color .2s,translate .2s;
}

.car-08__replay:hover,
.car-08__replay:focus-visible {
  background: var(--acc);
  color: oklch(0.18 0.03 220);
  translate: 0 -1px;
}

.car-08__replay:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 3px;
}

.car-08__split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(14px,2.5cqi,24px);
  padding: clamp(16px,3cqi,28px) clamp(16px,4cqi,40px);
  align-items: start;
}

.car-08__pane {
  border: 1px solid var(--line);
  border-radius: 16px;
  background: var(--panel);
  overflow: hidden;
}

.car-08__pane--raw {
  border-color: color-mix(in oklch,var(--bad) 45%,var(--line));
}

.car-08__pane--safe {
  border-color: color-mix(in oklch,var(--good) 45%,var(--line));
}

.car-08__panehead {
  display: flex;
  align-items: baseline;
  gap: 9px;
  padding: 13px 16px;
  border-bottom: 1px solid var(--line);
}

.car-08__panehead h2 {
  font-size: 14px;
  font-weight: 800;
  letter-spacing: -.01em;
}

.car-08__panehead span {
  font-size: 10.5px;
  color: var(--mut);
  margin-left: auto;
  text-align: right;
}

.car-08__dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  align-self: center;
}

.car-08__dot--bad {
  background: var(--bad);
  box-shadow: 0 0 10px var(--bad);
}

.car-08__dot--good {
  background: var(--good);
  box-shadow: 0 0 10px var(--good);
}

.car-08__feed {
  padding: 16px 16px 20px;
}

.car-08__feed h3 {
  font-size: 16.5px;
  letter-spacing: -.015em;
  line-height: 1.25;
}

.car-08__meta {
  font-size: 11.5px;
  color: var(--mut);
  margin: 5px 0 12px;
}

.car-08__feed p {
  font-size: 13px;
  line-height: 1.6;
  color: var(--mut);
  margin: 12px 0;
}

.car-08__img {
  display: block;
  width: 100%;
  border-radius: 10px;
  object-fit: cover;
  background: oklch(0.26 0.015 260);
}

.car-08__img--raw {
  height: auto;
}

.car-08__img--safe {
  aspect-ratio: 3/2;
  background: linear-gradient(110deg,oklch(0.26 0.015 260) 30%,oklch(0.33 0.02 260) 45%,oklch(0.26 0.015 260) 60%) 0 0/200% 100%;
  animation: car08-shimmer 1.3s linear infinite;
}

@keyframes car08-shimmer {
  to {
    background-position: -200% 0;
  }
}

.car-08__marker {
  font-weight: 700;
  color: var(--ink) !important;
  border-left: 3px solid var(--acc);
  padding-left: 10px;
}

.car-08__foot {
  padding: 16px clamp(16px,4cqi,40px) 30px;
  font-size: 12.5px;
  line-height: 1.7;
  color: var(--mut);
  border-top: 1px solid var(--line);
}

@container car08 (width < 700px) {
  .car-08__split {
    grid-template-columns: 1fr;
  }

  .car-08__cls {
    margin-left: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .car-08__img--safe {
    animation: none;
  }

  .car-08 * {
    transition-duration: .01ms !important;
  }
}
(() => {
  const root = document.querySelector('.car-08');
  if (!root || root.dataset.init) return;
  root.dataset.init = '1';
  const imgs = [...root.querySelectorAll('img[data-src]')];
  const meter = root.querySelector('.car-08__cls b');
  let cls = 0;
  try {
    new PerformanceObserver((list) => {
      list.getEntries().forEach((e) => { if (!e.hadRecentInput) cls += e.value; });
      meter.textContent = cls.toFixed(4);
    }).observe({ type: 'layout-shift', buffered: true });
  } catch (err) { meter.textContent = 'n/a'; }
  const run = () => imgs.forEach((im, i) => {
    im.removeAttribute('src');
    setTimeout(() => { im.src = im.dataset.src; }, 900 + (i % 2) * 500 + Math.floor(i / 2) * 150);
  });
  root.querySelector('.car-08__replay').addEventListener('click', run);
  run();
})();
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Aspect Ratio Demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: CSS Aspect Ratio Prevent Layout Shift Placeholder
Source: https://codefronts.com/layouts/css-aspect-ratio/css-aspect-ratio-prevent-layout-shift-placeholder/

A split-screen lab that makes Cumulative Layout Shift visible: the same article twice, images arriving on a simulated slow connection. The left pane reserves nothing — headlines lurch as each photo lands. The right pane gives every image aspect-ratio: 3/2 plus a shimmer skeleton — pixels pour into pre-reserved boxes and the text never moves. A live CLS meter (PerformanceObserver) keeps score, and a replay button re-runs the network as many times as you need to believe it.
## HTML
```html
<section class="car-08" aria-label="Prevent layout shift placeholder demo">
  <div class="car-08__stage" id="car08-top">
    <header class="car-08__bar">
      <div>
        <h1>Layout-shift lab</h1>
        <p>Same article, same slow network. One pane reserves image space with <code>aspect-ratio</code>; one doesn't.</p>
      </div>
      <p class="car-08__cls" aria-live="polite">CLS recorded <b>0.0000</b><span>Chromium only</span></p>
      <button class="car-08__replay" type="button">↻ Replay slow network</button>
    </header>
    <div class="car-08__split">
      <article class="car-08__pane car-08__pane--raw" aria-label="Without reservation">
        <header class="car-08__panehead"><b class="car-08__dot car-08__dot--bad" aria-hidden="true"></b><h2>Unreserved</h2><span>img { height: auto } — nothing else</span></header>
        <div class="car-08__feed">
          <h3>Trail report: the long way around Sorapiss</h3>
          <p class="car-08__meta">Ren Ito · 11 min read</p>
          <img data-src="https://images.unsplash.com/photo-1464822759023-fed622ff2c3b?q=80&w=1200&h=800&auto=format&fit=crop" width="1200" height="800" alt="Mountain ridge at dawn" class="car-08__img car-08__img--raw">
          <p>The photo above arrives late — and when it does, this paragraph gets shoved down by its full height. On a phone, that's the moment you tap the wrong link.</p>
          <img data-src="https://images.unsplash.com/photo-1501785888041-af3ef285b470?q=80&w=1200&h=800&auto=format&fit=crop" width="1200" height="800" alt="Lake at golden hour" class="car-08__img car-08__img--raw">
          <p class="car-08__marker">⬑ Watch this line jump on every replay.</p>
        </div>
      </article>
      <article class="car-08__pane car-08__pane--safe" aria-label="Reserved with aspect-ratio">
        <header class="car-08__panehead"><b class="car-08__dot car-08__dot--good" aria-hidden="true"></b><h2>Reserved</h2><span>img { aspect-ratio: 3/2 } + shimmer</span></header>
        <div class="car-08__feed">
          <h3>Trail report: the long way around Sorapiss</h3>
          <p class="car-08__meta">Ren Ito · 11 min read</p>
          <img data-src="https://images.unsplash.com/photo-1464822759023-fed622ff2c3b?q=80&w=1200&h=800&auto=format&fit=crop" width="1200" height="800" alt="Mountain ridge at dawn" class="car-08__img car-08__img--safe">
          <p>The box above existed at its final size before a single byte of image arrived. Pixels pour in; geometry holds; this paragraph has never moved.</p>
          <img data-src="https://images.unsplash.com/photo-1501785888041-af3ef285b470?q=80&w=1200&h=800&auto=format&fit=crop" width="1200" height="800" alt="Lake at golden hour" class="car-08__img car-08__img--safe">
          <p class="car-08__marker">⬑ This line has been exactly here since first paint.</p>
        </div>
      </article>
    </div>
    <footer class="car-08__foot">CLS is a Core Web Vitals ranking signal; 0.1 is the whole-page budget and one unreserved hero can spend it. Reserve with <code>aspect-ratio</code> (design-imposed shape) or <code>width</code>/<code>height</code> attributes (per-asset shape). A demo from the CodeFronts aspect-ratio collection.</footer>
  </div>
</section>
```
## CSS
```css
.car-08,
.car-08 *,
.car-08 *::before,
.car-08 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.car-08 {
  --bg: oklch(0.15 0.012 260);
  --panel: oklch(0.2 0.015 260);
  --ink: oklch(0.95 0.006 250);
  --mut: oklch(0.66 0.015 255);
  --line: oklch(0.31 0.02 260);
  --good: oklch(0.75 0.17 150);
  --bad: oklch(0.66 0.21 25);
  --acc: oklch(0.78 0.13 220);
  font-family: 'Segoe UI',system-ui,sans-serif;
  color: var(--ink);
  container-type: inline-size;
  display: block;
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  background: var(--bg);
}

.car-08__stage {
  width: 100%;
  container: car08/inline-size;
  height: 100vh;
  height: 100svh;
  overflow-y: auto;
  background: var(--bg);
}

.car-08__bar {
  display: flex;
  align-items: center;
  gap: 18px;
  flex-wrap: wrap;
  padding: 20px clamp(16px,4cqi,40px);
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  z-index: 5;
  background: color-mix(in oklch,var(--bg) 88%,transparent);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
}

.car-08__bar h1 {
  font-size: clamp(17px,2.4cqi,22px);
  letter-spacing: -.02em;
  font-weight: 800;
}

.car-08__bar p {
  font-size: 12.5px;
  color: var(--mut);
  margin-top: 3px;
  max-width: 52ch;
}

.car-08__bar code,
.car-08__foot code,
.car-08__panehead span {
  font-family: ui-monospace,Menlo,Consolas,monospace;
}

.car-08__bar code,
.car-08__foot code {
  font-size: .92em;
  background: oklch(0.26 0.02 260);
  padding: 2px 6px;
  border-radius: 5px;
  color: var(--acc);
}

.car-08__cls {
  margin-left: auto;
  text-align: right;
  font-size: 11px;
  color: var(--mut);
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
}

.car-08__cls b {
  font-family: ui-monospace,Menlo,monospace;
  font-size: 20px;
  color: var(--acc);
  font-weight: 700;
}

.car-08__replay {
  display: grid;
  place-items: center;
  min-height: 46px;
  padding: 0 18px;
  border-radius: 12px;
  border: 1.5px solid var(--acc);
  background: transparent;
  color: var(--acc);
  font: inherit;
  font-size: 13.5px;
  font-weight: 700;
  cursor: pointer;
  transition: background .2s,color .2s,translate .2s;
}

.car-08__replay:hover,
.car-08__replay:focus-visible {
  background: var(--acc);
  color: oklch(0.18 0.03 220);
  translate: 0 -1px;
}

.car-08__replay:focus-visible {
  outline: 2px solid var(--acc);
  outline-offset: 3px;
}

.car-08__split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(14px,2.5cqi,24px);
  padding: clamp(16px,3cqi,28px) clamp(16px,4cqi,40px);
  align-items: start;
}

.car-08__pane {
  border: 1px solid var(--line);
  border-radius: 16px;
  background: var(--panel);
  overflow: hidden;
}

.car-08__pane--raw {
  border-color: color-mix(in oklch,var(--bad) 45%,var(--line));
}

.car-08__pane--safe {
  border-color: color-mix(in oklch,var(--good) 45%,var(--line));
}

.car-08__panehead {
  display: flex;
  align-items: baseline;
  gap: 9px;
  padding: 13px 16px;
  border-bottom: 1px solid var(--line);
}

.car-08__panehead h2 {
  font-size: 14px;
  font-weight: 800;
  letter-spacing: -.01em;
}

.car-08__panehead span {
  font-size: 10.5px;
  color: var(--mut);
  margin-left: auto;
  text-align: right;
}

.car-08__dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  align-self: center;
}

.car-08__dot--bad {
  background: var(--bad);
  box-shadow: 0 0 10px var(--bad);
}

.car-08__dot--good {
  background: var(--good);
  box-shadow: 0 0 10px var(--good);
}

.car-08__feed {
  padding: 16px 16px 20px;
}

.car-08__feed h3 {
  font-size: 16.5px;
  letter-spacing: -.015em;
  line-height: 1.25;
}

.car-08__meta {
  font-size: 11.5px;
  color: var(--mut);
  margin: 5px 0 12px;
}

.car-08__feed p {
  font-size: 13px;
  line-height: 1.6;
  color: var(--mut);
  margin: 12px 0;
}

.car-08__img {
  display: block;
  width: 100%;
  border-radius: 10px;
  object-fit: cover;
  background: oklch(0.26 0.015 260);
}

.car-08__img--raw {
  height: auto;
}

.car-08__img--safe {
  aspect-ratio: 3/2;
  background: linear-gradient(110deg,oklch(0.26 0.015 260) 30%,oklch(0.33 0.02 260) 45%,oklch(0.26 0.015 260) 60%) 0 0/200% 100%;
  animation: car08-shimmer 1.3s linear infinite;
}

@keyframes car08-shimmer {
  to {
    background-position: -200% 0;
  }
}

.car-08__marker {
  font-weight: 700;
  color: var(--ink) !important;
  border-left: 3px solid var(--acc);
  padding-left: 10px;
}

.car-08__foot {
  padding: 16px clamp(16px,4cqi,40px) 30px;
  font-size: 12.5px;
  line-height: 1.7;
  color: var(--mut);
  border-top: 1px solid var(--line);
}

@container car08 (width < 700px) {
  .car-08__split {
    grid-template-columns: 1fr;
  }

  .car-08__cls {
    margin-left: 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .car-08__img--safe {
    animation: none;
  }

  .car-08 * {
    transition-duration: .01ms !important;
  }
}
```

## JavaScript
```js
(() => {
  const root = document.querySelector('.car-08');
  if (!root || root.dataset.init) return;
  root.dataset.init = '1';
  const imgs = [...root.querySelectorAll('img[data-src]')];
  const meter = root.querySelector('.car-08__cls b');
  let cls = 0;
  try {
    new PerformanceObserver((list) => {
      list.getEntries().forEach((e) => { if (!e.hadRecentInput) cls += e.value; });
      meter.textContent = cls.toFixed(4);
    }).observe({ type: 'layout-shift', buffered: true });
  } catch (err) { meter.textContent = 'n/a'; }
  const run = () => imgs.forEach((im, i) => {
    im.removeAttribute('src');
    setTimeout(() => { im.src = im.dataset.src; }, 900 + (i % 2) * 500 + Math.floor(i / 2) * 150);
  });
  root.querySelector('.car-08__replay').addEventListener('click', run);
  run();
})();
```

How this works

Layout shift is a reservation problem: until an image's pixels arrive, the browser doesn't know its height, renders it 0px tall, and then shoves everything down when the real size shows up. The fix is telling layout the shape before the network delivers it:

.feed img{ width:100%; height:auto;
  aspect-ratio:3 / 2;            /* the reservation */
  object-fit:cover;              /* honor it after arrival */
  background:linear-gradient(110deg,
    oklch(0.27 0.01 260) 30%, oklch(0.33 0.01 260) 45%,
    oklch(0.27 0.01 260) 60%) 0 0/200% 100%;
  animation:car08-shimmer 1.3s linear infinite; }  /* the skeleton */

With the ratio declared, the empty <img> is already a full-size box at first paint — the shimmer plays across the reserved area, and when the file lands, paint changes but geometry doesn't. That's the entire CLS fix: layout happens once. The equivalent HTML-only form is <img width='1200' height='800'>, which browsers map to an internal aspect-ratio:auto 1200/800; use attributes when the CMS knows each image's true size (per-image ratios), use the CSS property when the design imposes one shape on everything (uniform cards). Both belong in real pages — attributes describe, CSS overrides.

The meter is the receipts: a PerformanceObserver on layout-shift entries sums exactly what Chrome's Core Web Vitals report would — watch it climb as the left pane jolts, while the right pane contributes 0.000. Google's 'good' threshold is 0.1 for the whole page; a single unreserved hero image on a phone can spend that budget alone, which is why this one property has SEO consequences: CLS is a ranking signal, and aspect-ratio is its cheapest fix.

Make it yours

  • Match the real ratio: reserve 3/2 only if you'll crop to 3/2. Reserving one shape and delivering another just relocates the shift to image-arrival time.
  • Skeleton flavor: the shimmer is a moving background-position gradient — swap for a solid oklch(0.3 0 0) block for calm feeds, or a dominant-color placeholder piped in as an inline custom property from your CMS.
  • Network drama: the simulated delay is one number in the JS (900ms + 450ms per image). Set it to 3000 to demo on a projector, or wire the same markup to real lazy-loaded images in production.
  • Beyond images: the same reservation works for ad slots (demo 10), embeds (demo 01) and map iframes (demo 12) — anything third-party that reports its size late.

Gotchas — read before shipping

  • aspect-ratio reserves space only when the element has a definite width — an inline img with no width collapses anyway. width:100% (or the width attribute) is part of the fix, not decoration.
  • Reserving is not lying: if captions or aspect-mismatched files can change the final height, you've built a delayed shift. Reserve the true rendered shape, cropping with object-fit to enforce it.
  • The layout-shift PerformanceObserver is Chromium-only — the meter shows n/a in Safari/Firefox while the visual comparison still works everywhere.
  • Shifts caused by user input within 500ms are excluded from CLS by spec (hadRecentInput) — the replay button here doesn't game the score; the meter honestly counts the post-click image arrivals it causes.

Browser support

ChromeSafariFirefoxEdge
111+16.4+113+111+

The pattern (aspect-ratio + skeleton) is 2021-universal; only the CLS meter needs Chromium's layout-shift entry type and degrades to 'n/a' elsewhere. Floor reflects oklch()/color-mix() tokens.

Techniques used in this demo

Search CodeFronts

Loading…