Fix CLS Issues on Liferay (Layout Shift) | OpsBlu Docs

Fix CLS Issues on Liferay (Layout Shift)

Stabilize Liferay layouts by reserving portlet and fragment containers, sizing Document Library images, and preloading theme fonts.

General Guide: See Global CLS Guide for universal concepts and fixes.

What is CLS?

Cumulative Layout Shift measures visual stability. Google recommends CLS under 0.1. Liferay DXP generates CLS from portlet containers rendering at variable speeds, Content Page fragment loading, and Adaptive Media image variant swaps.

Liferay-Specific CLS Causes

  • Portlet container rendering -- portlets render independently at different speeds, causing their containers to resize
  • Content Page fragments -- fragments load and render sequentially, shifting surrounding content
  • Adaptive Media resolution switching -- low-resolution placeholder swaps to high-resolution variant
  • Cookie consent/alert portlets -- banner portlets injected at the top push all page content down
  • Theme font loading -- custom theme fonts cause FOUT shifts

Fixes

1. Reserve Space for Portlet Containers

/* Theme CSS -- portlet container sizing */
.portlet-boundary { min-height: 100px; contain: layout; }
.portlet-column { contain: layout; }

/* Specific portlet types */
.portlet-journal-content { min-height: 200px; }
.portlet-navigation { min-height: 60px; }
.portlet-asset-publisher { min-height: 300px; }

2. Stabilize Content Page Fragments

/* Fragment containers */
.page-editor__fragment { contain: layout; min-height: 100px; }
.lfr-layout-structure-item { contain: layout; }

/* Hero fragment */
[data-lfr-fragment-id="hero"] { min-height: 500px; aspect-ratio: 16/6; }

3. Fix Adaptive Media Image Sizing

Always include dimensions from the Document Library:

<#-- In theme FreeMarker templates -->
<img
  src="${imageUrl}"
  width="800" height="450"
  alt="${title}"
  loading="lazy"
  style="aspect-ratio: 800/450; width: 100%; height: auto;"
>

4. Fix Alert Banner CLS

/* Liferay alert banners -- overlay instead of push */
.alert-container, .lfr-alert-container {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 9999;
}

5. Preload Theme Fonts

<link rel="preload" href="${themeDisplay.getPathThemeCSS()}/fonts/brand.woff2"
      as="font" type="font/woff2" crossorigin />
@font-face {
  font-family: 'BrandFont';
  src: url('fonts/brand.woff2') format('woff2');
  font-display: swap;
}

Measuring CLS on Liferay

  1. Chrome DevTools Performance tab -- look for layout shifts during portlet rendering
  2. Test pages with multiple portlets -- higher portlet count = higher CLS risk
  3. Test authenticated vs anonymous -- different portlet sets = different CLS
  4. Liferay's built-in profiling for portlet render order

Analytics Script Impact

  • Liferay Analytics Cloud loads asynchronously -- minimal CLS
  • Custom analytics portlets should be in fixed-height containers
  • Cookie consent (GDPR module) should use fixed-position overlay