Implementing Critical CSS Inlining for Above-the-Fold Content

Table of Contents
Big thanks to our contributors those make our blogs possible.

Our growing community of contributors bring their unique insights from around the world to power our blog. 

Introduction

Page speed drives engagement: 47% of users expect a page to load in 2 seconds or less, and 53% will abandon a site that takes more than 3 seconds. One of the biggest render‑blocking culprits is CSS—browsers block painting until all external stylesheets are downloaded and parsed. Critical CSS inlining extracts only the styles needed to render the initial viewport (above‑the‑fold) and inlines them directly into the HTML <head>. The remaining CSS loads asynchronously, eliminating render‑blocking and slashing Largest Contentful Paint (LCP) times.

This guide covers:

  1. Defining “Above‑the‑Fold”—choosing viewports and elements
  2. Extracting Critical CSS—manual vs. automated (Penthouse, Critters)
  3. Inlining Techniques—HTML <style>, media/onload, <link preload>
  4. Build Pipeline Integration—Gulp, Webpack, CI/CD automation
  5. Asynchronous Loading—best patterns for deferred styles
  6. Testing & Monitoring—ensuring coverage and performance gains
  7. Best Practices & Pitfalls—keeping critical CSS lean and up‑to‑date

1. Defining “Above‑the‑Fold” for Your Site

1.1 Select Target Viewports

Decide which screen sizes to optimize first:

Device TypeViewport Size (px)Notes
Desktop1300 × 900Common 1366×768 with margins
Tablet768 × 1024Portrait orientation
Mobile375 × 667 (iPhone X)Or 360 × 640 for many Androids

Generate distinct critical CSS for drastically different layouts (e.g., mobile vs. desktop).

1.2 Identify Critical Elements

List the components visible before scrolling:

  • Header (logo, nav bar)
  • Hero banner or main image
  • Primary headline/subhead
  • Key call‑to‑action button
  • Above‑fold form fields or feature panels

Anything not in that top region can rely on deferred CSS.

2. Extracting Critical CSS

2.1 Manual Extraction (Small Sites)

  1. Open DevTools → Elements → select above‑fold elements.
  2. In Computed Styles, copy rules into a critical.css file.
  3. Minify with csso or an online minifier.

Pros: Full control; no new dependencies.
Cons: Tedious; drifts as code changes.

2.2 Automated Tools

2.2.1 Penthouse (Node.js)

Renders your page in Headless Chrome and extracts only used CSS for a given viewport.

jsCopyEdit// scripts/critical.js
const penthouse = require('penthouse');
const fs = require('fs');

penthouse({
  url: 'http://localhost:8080',
  css: 'dist/styles.css',
  width: 1300,
  height: 900
})
.then(css => fs.writeFileSync('dist/critical.css', css))
.catch(err => console.error(err));

Usage:

bashCopyEditnpm run build         # generate dist/styles.css
node scripts/critical.js

Generate separate critical.mobile.css by passing width: 375, height: 667.

2.2.2 Critters (Webpack Plugin)

Automatically inlines critical CSS at build time.

jsCopyEdit// webpack.config.js
const Critters = require('critters-webpack-plugin');

module.exports = {
  // ... other config ...
  plugins: [
    new Critters({
      preload: 'js',       // or 'media', 'swap', false
      pruneSource: true,   // remove inlined CSS from external file
      noscriptFallback: true
    })
  ]
};

After build, your HTML will include <style> blocks for critical CSS and defer the rest.

3. Inlining Critical CSS in HTML

3.1 Direct <style> Injection

Embed the contents of critical.css into your HTML:

htmlCopyEdit<head>
  <style>
    /* dist/critical.css */
    header { display: flex; align-items: center; }
    .hero { background: url(/hero.jpg) center/cover; height: 600px; }
    /* …other critical rules… */
  </style>

  <!-- Deferred stylesheet -->
  <link rel="stylesheet" href="styles.css" media="print" onload="this.media='all'">
  <noscript>
    <link rel="stylesheet" href="styles.css">
  </noscript>
</head>

Pattern Explanation:

  • media="print" prevents blocking;
  • onload="this.media='all'" switches to normal after load;
  • <noscript> fallback for non‑JS users.

3.2 <link rel="preload"> Technique

Modern browsers support preloading styles then swapping:

htmlCopyEdit<link rel="preload" href="styles.css" as="style" onload="this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>

This hints the browser to fetch CSS early, but avoids blocking critical render.

4. Build Pipeline Integration

4.1 Gulp Task

jsCopyEdit// gulpfile.js
const gulp = require('gulp');
const critical = require('critical').stream;

gulp.task('critical', () => {
  return gulp.src('dist/index.html')
    .pipe(critical({ base: 'dist/', inline: true, css: ['dist/styles.css'] }))
    .pipe(gulp.dest('dist'));
});

// Run: gulp critical after your normal build

This inlines <style> tags and inserts deferred <link>s automatically.

4.2 Webpack + Critters

See section 2.2.2; Critters runs as part of HTMLWebpackPlugin to inline CSS.

4.3 CI/CD Automation

  1. Build your assets.
  2. Generate critical CSS (Penthouse or Critters).
  3. Inject into HTML (Gulp/Critters).
  4. Deploy updated files.

Add these steps to your CI script (GitHub Actions, GitLab CI, etc.) to run on every push.

5. Loading Non‑Critical CSS Asynchronously

TechniqueProsCons
media="print" + JSBroad compatibilityRequires JS to switch media
<link preload> swapBrowser‑managed, no media hackOlder browsers may ignore preload
JS injectionFull controlFlash of unstyled content if delayed

Choose based on your browser support requirements.

6. Testing & Monitoring

6.1 Coverage Verification

In Chrome DevTools → Coverage panel:

  • Run coverage; verify that all inlined CSS rules are marked “used” for above-fold elements.
  • Unused in critical inlining indicates over‑inclusion; refine selectors.

6.2 Performance Audits

  • Lighthouse: measure LCP, First Contentful Paint before & after inlining.
  • WebPageTest: inspect waterfall to ensure no render‑blocking CSS loads before paint.

6.3 Real‑User Monitoring

Capture Web Vitals (LCP, FCP) in production to confirm improvements across geographies and devices.

7. Best Practices & Pitfalls

  1. Keep It Small: Aim for < 14 KB of critical CSS to minimize HTML payload.
  2. Automate Regularly: Integrate extraction into build or CI to avoid drift.
  3. Separate Mobile/Desktop: If layouts differ, generate two critical CSS sets and conditionally inline based on UA or responsive <style> blocks.
  4. Avoid Complex Selectors: Critical CSS should target broad, high‑level components, not deeply nested rules.
  5. Prune Redundancy: Use tools (UnCSS, PurgeCSS) downstream of inlining to remove unused rules from the deferred stylesheet.
  6. Test Fail-Safes: Ensure fallback <noscript> links and check performance in no‑JS scenarios.

Conclusion

Critical CSS inlining is a high-impact optimization that dramatically reduces render‑blocking, accelerates LCP, and boosts perceived performance. By:

  • Identifying your above‑the‑fold elements and viewports
  • Extracting only essential styles (manually or via Penthouse/Critters)
  • Inlining them into your HTML <head>
  • Deferring the rest asynchronously

—and automating the process in your build pipeline—you’ll deliver a lightning‑fast first paint for your users. Start by measuring your current CSS footprint, integrate a critical CSS tool, and monitor Web Vitals to quantify the gains. Each millisecond shaved off your render path improves engagement, retention, and ultimately, conversions.

Let's connect on TikTok

Join our newsletter to stay updated

Sydney Based Software Solutions Professional who is crafting exceptional systems and applications to solve a diverse range of problems for the past 10 years.

Share the Post

Related Posts