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

Fix CLS Issues on Bludit (Layout Shift)

Stabilize Bludit flat-file layouts by setting image dimensions in templates, preloading theme fonts, and constraining plugin output.

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. Bludit's flat-file architecture means CLS issues come from theme templates, plugin-injected elements, and missing image dimensions rather than server-side dynamic content.

Bludit-Specific CLS Causes

  • Cover images without dimensions -- Bludit's $page->coverImage() outputs <img> tags without width/height in most default themes
  • Plugin output injection -- plugins like cookie consent, social sharing, and notification bars inject elements into the DOM after initial render
  • Theme font loading -- custom themes loading Google Fonts via @import cause FOUT and text reflow
  • Sidebar widget reflow -- Bludit plugins that render sidebar content (tag clouds, recent posts) load asynchronously
  • Ad injection -- manually placed ad code in theme templates without reserved space

Fixes

1. Add Image Dimensions in Theme Templates

Most Bludit themes output cover images without size attributes. Fix in your theme's page template:

<!-- themes/yourtheme/php/page.php -->
<?php if ($page->coverImage(true)): ?>
  <?php
    $imgPath = PATH_UPLOADS . basename($page->coverImage(true));
    $imgSize = file_exists($imgPath) ? getimagesize($imgPath) : [1200, 630];
  ?>
  <div class="cover-image" style="aspect-ratio: <?php echo $imgSize[0]; ?>/<?php echo $imgSize[1]; ?>;">
    <img
      src="<?php echo $page->coverImage(true); ?>"
      width="<?php echo $imgSize[0]; ?>"
      height="<?php echo $imgSize[1]; ?>"
      alt="<?php echo $page->title(); ?>"
      style="width: 100%; height: auto;"
    >
  </div>
<?php endif; ?>

For blog listing pages where multiple post thumbnails load:

<!-- themes/yourtheme/php/blog.php -->
<?php foreach ($content as $page): ?>
  <article class="post-card">
    <?php if ($page->coverImage(true)): ?>
      <div class="post-thumbnail" style="aspect-ratio: 16/9; overflow: hidden;">
        <img
          src="<?php echo $page->coverImage(true); ?>"
          width="400" height="225"
          alt="<?php echo $page->title(); ?>"
          loading="lazy"
          style="width: 100%; height: 100%; object-fit: cover;"
        >
      </div>
    <?php endif; ?>
    <h2><?php echo $page->title(); ?></h2>
  </article>
<?php endforeach; ?>

2. Constrain Plugin Output Areas

Reserve space for plugin-injected content in your theme's CSS:

/* Reserve space for cookie consent plugin */
.plugin-cookie-consent {
  position: fixed; /* Use fixed instead of relative to avoid pushing content */
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 9999;
}

/* Reserve sidebar plugin areas */
.sidebar .plugin-widget {
  min-height: 100px;
  contain: layout;
}

/* Social sharing bar -- prevent injection shift */
.social-share-container {
  min-height: 40px;
  contain: layout;
}

3. Fix Font Loading in Themes

Bludit themes commonly load fonts with @import which blocks rendering:

<!-- BAD: In themes/yourtheme/css/style.css -->
<!-- @import url('https://fonts.googleapis.com/css?family=Open+Sans:400,700'); -->

<!-- GOOD: In themes/yourtheme/php/head.php -->
<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" />
/* Add fallback metrics to minimize text reflow */
body {
  font-family: 'Open Sans', 'Open Sans Fallback', Arial, sans-serif;
}

@font-face {
  font-family: 'Open Sans Fallback';
  src: local('Arial');
  size-adjust: 105%;
  ascent-override: 110%;
  descent-override: 30%;
}

4. Prevent Inline Content Injection

If your theme dynamically injects notifications or banners via JavaScript:

// BAD: Inserting at top of body pushes everything down
document.body.insertBefore(banner, document.body.firstChild);

// GOOD: Use a pre-reserved slot in your theme template
<div id="notification-slot" style="min-height: 0; transition: min-height 0.3s;">
</div>

<script>
  // Smoothly expand the slot instead of pushing content
  var slot = document.getElementById('notification-slot');
  slot.innerHTML = '<div class="notification">Your message here</div>';
  slot.style.minHeight = '50px';
</script>

Measuring CLS on Bludit

  1. Chrome DevTools Performance tab -- record a page load, then filter the "Experience" lane for layout shifts. Click each shift to see which DOM node moved.
  2. PageSpeed Insights -- run on both homepage (blog listing) and individual post pages
  3. Test with plugins disabled -- Bludit's Settings > Plugins lets you disable plugins individually; test CLS after each disable to find the culprit
  4. Mobile testing -- Bludit themes are often desktop-first, with mobile layouts that have more CLS from reflowing images and sidebars

Analytics Script Impact

  • Bludit's Simple Stats plugin -- server-side only, no CLS impact
  • Manually added analytics -- if placed in head.php, ensure they do not inject visible elements (tracking pixels are fine, but chat widgets or survey popups cause CLS)
  • Ad code -- any manually placed AdSense or ad network code needs explicit min-height containers; Bludit has no ad management plugin to handle this automatically