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. CMS Made Simple (CMSMS) generates CLS from unsized images in Smarty templates, dynamically-loaded module content, and theme font loading.
CMSMS-Specific CLS Causes
- Images in content blocks without dimensions -- the WYSIWYG editor (TinyMCE/MicroTiny) does not always save
width/heighton images - News/Gallery module output -- these modules inject content with varying heights depending on entry count and image sizes
- Theme font loading -- CMSMS themes typically load Google Fonts via
@importin CSS files, causing FOUT - FormBuilder dynamic rendering -- forms inserted via
{FormBuilder}tags render after page load if using AJAX - Ad/embed content in content blocks -- editors paste iframe embeds without dimensions
Fixes
1. Add Image Dimensions in Smarty Templates
Override the default content output to include dimensions:
{* In your page template -- wrap content images with aspect ratio *}
<div class="page-content">
{$content}
</div>
{* For explicit image fields *}
{if $content_obj->GetPropertyValue('hero_image')}
<div style="aspect-ratio: 16/9; overflow: hidden;">
<img
src="{uploads_url}/images/{$content_obj->GetPropertyValue('hero_image')}"
width="1200" height="675"
alt="{$content_obj->Name()|cms_escape}"
style="width: 100%; height: 100%; object-fit: cover;"
>
</div>
{/if}
Add CSS to handle images in WYSIWYG content that lack dimensions:
/* Fix unsized images in content blocks */
.page-content img {
max-width: 100%;
height: auto;
}
.page-content img:not([width]) {
aspect-ratio: 16 / 9;
width: 100%;
object-fit: cover;
}
2. Constrain Module Output Containers
Wrap module output in fixed-height containers:
{* News module summary list *}
<div class="news-container" style="min-height: 400px; contain: layout;">
{News action='default' number=5 detailtemplate='news_detail' summarytemplate='news_summary'}
</div>
{* Gallery module *}
<div class="gallery-container" style="min-height: 500px; contain: layout;">
{Gallery action='default' gallery='portfolio'}
</div>
/* Consistent card heights in News listings */
.news-summary-item {
min-height: 200px;
contain: layout;
}
/* Gallery grid items */
.gallery-item {
aspect-ratio: 1 / 1;
overflow: hidden;
}
.gallery-item img {
width: 100%;
height: 100%;
object-fit: cover;
}
3. Fix Font Loading
Move font loading from CSS @import to HTML <link> with preconnect:
{* In your layout template <head> *}
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;700&display=swap" rel="stylesheet">
/* Size-adjust fallback to minimize text shift */
@font-face {
font-family: 'Open Sans Fallback';
src: local('Arial');
size-adjust: 105%;
ascent-override: 110%;
}
body { font-family: 'Open Sans', 'Open Sans Fallback', Arial, sans-serif; }
4. Handle Embed Content
Add CSS rules for common embeds pasted into CMSMS content blocks:
/* Responsive iframe embeds */
.page-content iframe {
aspect-ratio: 16 / 9;
width: 100%;
height: auto;
border: 0;
}
/* Social embeds */
.page-content .twitter-tweet,
.page-content .instagram-media {
min-height: 400px;
contain: layout;
}
5. Fix FormBuilder CLS
Forms injected via {FormBuilder} can cause shifts:
/* Reserve space for form containers */
.formbuilder-form {
min-height: 300px;
contain: layout;
}
Measuring CLS on CMSMS
- Chrome DevTools Performance tab -- record and filter for layout shifts
- PageSpeed Insights -- test pages with News listings, Gallery output, and FormBuilder forms
- Test with modules disabled -- temporarily remove module calls from templates to isolate which module causes CLS
- Mobile testing -- CMSMS themes often have responsive layouts that reflow differently on mobile
Analytics Script Impact
- CMSMS analytics are typically added via UDTs or template code -- ensure they do not inject visible elements
- Cookie consent modules should use
position: fixedoverlays - Avoid analytics that inject survey popups or feedback widgets without fixed positioning