How to Fix CLS: Stop Elements Jumping on Your Pages
Elements jump while your page loads? Learn the unique CLS causes—images without size, late fonts, and unreserved embeds—plus numbered DIY fixes and when Fixwebnode should take over.
If buttons, images, or text leap around while your page is still loading, visitors mis-tap links and Core Web Vitals suffer. That jumpiness is Cumulative Layout Shift (CLS). This guide shows homeowners and small-business owners how to diagnose the real culprits, apply safe DIY fixes, and know when a specialist pass is smarter than more trial and error.
Fixwebnode works on production sites every week to stabilise layout shift—not with vague “speed tips,” but with measured layout fixes. Start with the practical steps below, or jump straight to the full service page for How to Fix Cumulative Layout Shift (CLS) Elements Jumping on Your Pages if you already know the problem is deeper than a missing image size.
Why CLS matters on real business pages
CLS scores how much visible content moves after it first appears. Google reports it in Search Console and PageSpeed Insights; shoppers feel it when a “Buy” button slides under their finger. A single late hero image, a webfont swap, or a cookie banner that drops in without reserved height can push your CLS over the “good” threshold (0.1) even when LCP looks fine.
You do not need a rewrite to fix most shifts. You need correct dimensions, reserved space, and stable injection order. The issues below are the ones we see most often on small-business WordPress and static sites—including local trade and construction sites that lean on galleries and quote forms.
Common CLS issues (unique symptoms)
These are distinct root causes. Treat the one that matches your symptoms first.
- Images and videos without width/height (or aspect-ratio) — Hero photos, product thumbs, and logo SVGs load, then shove text and CTAs downward once the browser knows their real size.
- Web fonts that swap late (FOIT/FOUT) — Body copy or headings reflow when the custom font finally paints, especially on mobile with slow connections.
- Late UI: cookie bars, promo strips, chat widgets — A banner or Intercom-style bubble injects after first paint and pushes the whole page down.
- Embeds and iframes without a reserved box — YouTube, Google Maps, calendars, and form iframes expand from 0 height when the third-party script finishes.
- Async content and ACF-driven blocks without skeleton space — Cards, testimonials, or CPT grids that hydrate after JS/CSS arrives and reflow the grid.
Issue 1 — Images and media without reserved dimensions
Symptom: PageSpeed flags “Image elements do not have explicit width and height.” Layout Shift Regions in Chrome light up on the hero or gallery as soon as the file arrives.
Step 1 — Measure the live shift
Open the page in Chrome, open DevTools → Performance, enable Layout Shift Regions and Experience, reload, and note which frames turn blue. Confirm with Lighthouse (lab) and Search Console (field) so you are not chasing a one-off.
npx lighthouse https://YOUR-SITE.example/ --only-categories=performance --view
# or, if lighthouse is installed globally:
lighthouse https://YOUR-SITE.example/ --form-factor=mobile --screenEmulation.mobile --output=html --output-path=./cls-report.html
Step 2 — Set intrinsic size on every <img> and <video>
Add matching width and height attributes (pixel values of the source file). Modern browsers use them only for aspect ratio, so responsive CSS still works.
<img
src="/media/hero-workshop.webp"
width="1600"
height="900"
alt="Workshop interior"
loading="lazy"
decoding="async"
>
Step 3 — Lock ratio in CSS for fluid layouts
img, video {
max-width: 100%;
height: auto;
}
.hero-media {
aspect-ratio: 16 / 9;
width: 100%;
overflow: hidden;
}
.hero-media img {
width: 100%;
height: 100%;
object-fit: cover;
}
Step 4 — Fix CMS theme output
In WordPress, ensure featured images and Gutenberg image blocks output dimensions. Strip theme CSS that forces height: auto without a ratio on containers that previously had fixed height. Re-run Lighthouse and confirm the image-related layout-shift entries drop.
When to call Fixwebnode: theme builders that strip attributes on save, multi-breakpoint art direction, or CDN transforms that serve different crops without updating ratio metadata.
Issue 2 — Web font swaps that reflow text
Symptom: Headings “snap” to a different weight or width a second after load; CLS attribution points at text nodes, not images.
Step 1 — Audit font loading
# List @font-face rules and display strategy in the critical CSS/HTML
curl -sL https://YOUR-SITE.example/ | grep -i "font-face\|font-display\|fonts.googleapis"
Step 2 — Use font-display: optional or swap with matched fallbacks
@font-face {
font-family: "OwnerSans";
src: url("/fonts/ownersans.woff2") format("woff2");
font-weight: 400 700;
font-style: normal;
font-display: optional; /* or swap if branding must always show */
size-adjust: 100%;
ascent-override: 95%;
descent-override: 20%;
line-gap-override: 0%;
}
Step 3 — Preload only the critical face
<link rel="preload" href="/fonts/ownersans.woff2" as="font" type="font/woff2" crossorigin>
Step 4 — Metric-match the system fallback
Pick a system stack whose average character width is close to your brand font, then tune size-adjust until the fallback and webfont occupy nearly the same line boxes. Reload on a throttled mobile profile and watch Layout Shift Regions on heading blocks—they should stay quiet.
When to call Fixwebnode: multiple variable fonts, icon fonts mixed with UI text, or a builder theme that injects Google Fonts without display control.
Issue 3 — Cookie bars, promo strips, and chat widgets
Symptom: First contentful paint looks stable, then a consent bar or announcement ribbon appears at the top and shoves everything down. Field CLS spikes on landing pages more than on interior pages.
Step 1 — Identify the injector
In DevTools → Network, filter for third-party scripts (CMP, chat, promo). In Performance, note the layout-shift event timestamp and the node that moved.
Step 2 — Reserve space before the script runs
/* Reserve a stable slot; adjust min-height to the real bar height */
.site-announcement-slot {
min-height: 48px; /* match final bar */
width: 100%;
box-sizing: border-box;
}
/* Prefer overlay bars that do not push document flow */
.cookie-banner {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 9999;
/* fixed/sticky overlays usually avoid CLS on the document */
}
Step 3 — Load non-critical widgets after interaction or idle
<script>
window.addEventListener('load', function () {
requestIdleCallback(function () {
var s = document.createElement('script');
s.src = 'https://cdn.example.com/chat-widget.js';
s.async = true;
document.body.appendChild(s);
});
});
</script>
Step 4 — Verify
Hard-reload with cache disabled. The reserved slot should hold height even if the script fails. CLS attribution for the banner node should disappear or fall under 0.1 combined.
When to call Fixwebnode: CMP tools that only offer “top push” modes, multi-language bars with variable height, or tag-manager soup you cannot edit safely.
Issue 4 — Embeds and iframes without an aspect-ratio box
Symptom: A map or video section collapses to a thin strip, then balloons open when the embed responds—classic on contact and project pages.
Step 1 — Wrap every embed in a ratio box
<div class="embed-slot" style="--embed-ratio: 16 / 9">
<iframe
src="https://www.youtube.com/embed/VIDEO_ID"
title="Project walkthrough"
loading="lazy"
allowfullscreen
></iframe>
</div>
.embed-slot {
position: relative;
width: 100%;
aspect-ratio: var(--embed-ratio, 16 / 9);
background: #f3f3f3; /* visible placeholder */
}
.embed-slot iframe {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
border: 0;
}
Step 2 — Prefer intrinsic placeholders for maps
If the map is below the fold, show a static image or coloured block with the same ratio and hydrate the iframe on click (“Load map”). That removes third-party layout timing from the critical path entirely.
Step 3 — Re-test mobile
npx lighthouse https://YOUR-SITE.example/contact/ \
--form-factor=mobile \
--only-categories=performance \
--output=json --output-path=./contact-cls.json
node -e "const r=require('./contact-cls.json'); console.log('CLS', r.audits['cumulative-layout-shift'].displayValue)"
When to call Fixwebnode: nested page-builder columns that fight aspect-ratio, or multiple third-party forms stacked without a shared skeleton.
Issue 5 — Async cards, CPT grids, and ACF-driven blocks
Symptom: A testimonials row or project grid pops in after JS, changing card heights and shifting the footer. Common on custom post type archives and flexible layout pages.
Teams that already run structured layouts—such as the patterns used in Advanced Custom Fields & CPT Developer Prahran | Scalable Layouts—can usually fix this by defining min-heights and skeleton states in the same field group that defines the cards.
Step 1 — Give every dynamic region a min-height skeleton
.cpt-card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
gap: 1.25rem;
}
.cpt-card {
min-height: 22rem; /* match average final card */
}
.cpt-card.is-loading {
background: linear-gradient(90deg, #eee, #f7f7f7, #eee);
background-size: 200% 100%;
}
Step 2 — Avoid inserting nodes above existing content
Append new cards into a pre-sized container; do not inject a whole new section between the hero and the next block after paint. If content must stream in, keep the container height stable and scroll internally if needed.
Step 3 — Defer non-critical CSS that defines card chrome
Critical CSS should include grid gaps and min-heights. Decorative card CSS can load async without changing geometry.
Step 4 — Verify with a cold cache on mobile throttling
Use DevTools → Network → Slow 4G, disable cache, reload three times, and average the CLS from the Experience track. Target a combined CLS under 0.1.
When to call Fixwebnode: flexible ACF page builders with dozens of layouts, or construction portfolio sites that mix galleries, specs tables, and quote CTAs—the same class of work covered in Website Solutions for Canberra Builders & Construction Specialists.
When DIY is enough vs when to book Fixwebnode
DIY is enough when Lighthouse points at a handful of images, one font family, or a single embed, and you can edit theme templates or CSS directly. Apply the numbered steps, re-measure on mobile, and watch Search Console’s Core Web Vitals report over 28 days.
Book a specialist when shifts come from tag managers, page-builder stacks, multi-template CPT archives, or third-party scripts you cannot configure. That is also the right call if field data stays “Needs improvement” after your fixes—lab scores can look green while real phones still shift.
Fixwebnode handles measured CLS remediation across the metros we already support. See where that coverage lands on the All service areas page if you are checking fit for your region before you send URLs.
Book a CLS stabilisation conversation
Send the URLs that jump, a Search Console screenshot if you have one, and any theme or builder constraints. We will tell you whether a short CSS/template pass is enough or whether fonts, embeds, and dynamic blocks need a coordinated fix.
Start here: How to Fix Cumulative Layout Shift (CLS) Elements Jumping on Your Pages. Bring the problem page list—we will map shifts to concrete layout changes, not generic “speed packages.”