# CLS prevention patterns are in use

- **Pattern:** `ab-001693` (`marketing-advanced-seo.mobile-performance-seo.cls-prevention`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001693
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001693)

## Why it matters

Cumulative Layout Shift (CLS) is a Core Web Vital — a score above 0.1 degrades Page Experience ranking on mobile. The most common cause is images rendered without explicit `width` and `height` attributes: the browser reserves no space for the image before it loads, so the page shifts downward when the image appears, displacing content the user was reading. A secondary CLS source is cookie consent banners or chat widgets that inject above existing content post-render, pushing the viewport. Beyond ranking, high CLS creates a frustrating user experience where buttons move before users can click them — a direct conversion impact measurable in A/B tests. iso-25010:2011 performance-efficiency covers both the ranking and usability dimensions.

## Severity rationale

High because CLS above 0.1 is a confirmed Core Web Vital failure that degrades Page Experience ranking and produces a measurably worse user experience that affects engagement and conversion rates.

## Remediation

Set explicit `width` and `height` on every `<Image>` component or use the `fill` prop with a sized container. For dynamically sized images, use the CSS `aspect-ratio` property to reserve space before the image loads.

```tsx
// components/post-image.tsx — reserve space before load
import Image from 'next/image'

// Option 1: explicit dimensions
<Image src={src} alt={alt} width={800} height={450} />

// Option 2: fill with sized container
<div style={{ position: 'relative', aspectRatio: '16/9' }}>
  <Image src={src} alt={alt} fill style={{ objectFit: 'cover' }} />
</div>
```

For cookie banners and chat widgets that inject above content: use `position: fixed` overlays that overlay rather than push content, or render a fixed-height placeholder server-side so the space is already reserved when the JS hydrates. In `app/globals.css`, verify no layout-affecting CSS transitions (`height`, `margin`, `top`) run on content elements — replace them with `transform: translateY()` equivalents which do not trigger layout.

## Detection

- **ID:** `cls-prevention`
- **Severity:** `high`
- **What to look for:** Count all image and media elements. Enumerate which have explicit width/height or aspect-ratio CSS vs. which could cause layout shifts. Cumulative Layout Shift (CLS) measures visual stability. Check for CLS risk patterns: (1) images without explicit `width` and `height` attributes — images without dimensions cause layout shifts when they load, (2) dynamic content injection above existing content (cookie notices, chat widgets, announcement bars that push page content down after initial render), (3) font loading without `font-display: swap` or `optional` — text reflow during font swap shifts layout, (4) CSS animations using layout-affecting properties (`height`, `top`, `margin`, `padding`) on visible content elements instead of `transform` and `opacity`. Check whether Next.js Image components provide explicit dimensions.
- **Pass criteria:** All `<img>` and `<Image>` elements have explicit `width` and `height` attributes, or use `fill` layout with a sized container. No unsized content injection above the main content area. Font loading is optimized. At least 90% of images and media elements must have explicit dimensions or aspect-ratio CSS to prevent layout shifts.
- **Fail criteria:** Multiple images lack `width` and `height` attributes. Cookie banners or chat widgets inject above main content without reserved space. CSS animations on content use layout-affecting properties.
- **Skip (N/A) when:** No images or dynamic content injection patterns detected. Project is API-only.

- **Cross-reference:** For LCP optimization, see `lcp-optimization`.
- **Detail on fail:** `"CLS risk: 8 images in components/ lack explicit width/height attributes. Cookie consent banner dynamically injects above main content without reserved space. Layout-affecting CSS transitions detected on content elements."`
- **Remediation:** CLS under 0.1 is considered good by Google. Fix the most common causes: always specify `width` and `height` on images (or use a `fill` layout with a sized container). For banner injections, reserve the space before the banner loads using a fixed-height placeholder, or use a `position: fixed` overlay that does not push content down. Replace layout-affecting CSS transitions with `transform`-based animations. For a complete performance analysis, the Performance & Load Readiness audit covers Core Web Vitals in detail.

  ```tsx
  // components/image.tsx — prevent CLS with explicit dimensions
  <Image src="/photo.webp" alt="Photo" width={800} height={600} />
  // Or CSS: .image-container { aspect-ratio: 16/9; }
  ```

## External references

- iso-25010 performance-efficiency

Taxons: performance, findability

HTML version: https://auditbuffet.com/patterns/ab-001693
