Critical CSS is the minimal set of above-the-fold styles required to render the visible portion of a page without blocking the browser on the full stylesheet download — inlining it in a <style> block in <head> eliminates the render-blocking stylesheet request and directly improves Largest Contentful Paint (LCP). Google PageSpeed Insights reports “Eliminate render-blocking resources” as one of the highest-impact recommendations for WordPress sites, and critical CSS inlining is the primary technique for addressing it on CSS-heavy themes. Generating critical CSS manually is impractical — the critical npm package uses Puppeteer to load each page type, extract the above-the-fold styles, and output the result as a string suitable for inlining. The full stylesheet is then loaded asynchronously using the rel="preload" + onload pattern, ensuring it applies before the user scrolls without blocking the initial render. WordPress’s wp_add_inline_style() inlines CSS after a given stylesheet handle, but for critical CSS the better approach is to output the <style> block directly in wp_head at priority 1, before any enqueued stylesheets. Cache the per-page critical CSS string in a transient keyed by the page URL — regenerating it on every request defeats the performance purpose entirely. Separate critical CSS sets for homepage, single post, archive, and WooCommerce shop improve accuracy compared to a single global critical CSS file that must cover all page types. The font-display: swap CSS property for Google Fonts prevents invisible text during font loading and complements critical CSS by ensuring layout-relevant font metrics are available from the first render cycle. The defer JS post applies the same render-blocking elimination principle to scripts that critical CSS applies to stylesheets — combine both for maximum LCP improvement. The browser caching post ensures the full stylesheet, once loaded asynchronously, is cached in the browser and not re-downloaded on subsequent page views. Re-generate critical CSS after every major theme update — theme changes alter the above-the-fold selector set and stale critical CSS causes layout shifts visible during the first render.
Problem: WordPress themes load full stylesheets synchronously in <head>, blocking the browser from rendering any content until the entire CSS file is downloaded and parsed — causing poor Largest Contentful Paint scores on mobile connections and triggering PageSpeed "render-blocking resources" warnings.
Solution: Extract above-the-fold critical CSS using the critical npm package, inline it via wp_head at high priority, and load the full stylesheet asynchronously with rel="preload" and a JavaScript onload fallback.
// 1. Generate critical CSS (run in theme directory)
// npm install --save-dev critical
// node generate-critical.js
// generate-critical.js
const critical = require('critical');
const pages = [
{ url: 'https://helloadmin.com/', out: 'critical-home.css' },
{ url: 'https://helloadmin.com/?p=1322', out: 'critical-single.css' },
];
pages.forEach(({ url, out }) => {
critical.generate({
src: url,
width: 1300,
height: 900,
dest: 'assets/critical/' + out,
inline: false,
extract: false,
penthouse: { timeout: 30000 },
}).then(() => console.log('Generated:', out));
});
// 2. Inline critical CSS and async-load full stylesheet in functions.php
add_action('wp_head', function() {
$template = is_singular() ? 'single' : 'home';
$file = get_theme_file_path("assets/critical/critical-{$template}.css");
if (!file_exists($file)) return;
$cache_key = 'critical_css_' . $template;
$css = get_transient($cache_key);
if (!$css) {
$css = file_get_contents($file);
set_transient($cache_key, $css, WEEK_IN_SECONDS);
}
echo '<style id="critical-css">' . $css . '</style>' . "
";
}, 1);
// Dequeue blocking stylesheet and reload it asynchronously
add_filter('style_loader_tag', function(string $tag, string $handle): string {
if ($handle !== 'mytheme-style') return $tag;
// Convert blocking link to preload + noscript fallback
$preload = str_replace("rel='stylesheet'", "rel='preload' as='style' onload="this.onload=null;this.rel='stylesheet'"", $tag);
$noscript = '<noscript>' . $tag . '</noscript>';
return $preload . $noscript;
}, 10, 2);
NOTE: The onload="this.rel='stylesheet'" pattern requires JavaScript — always include the <noscript> fallback so the full stylesheet loads for users with JavaScript disabled, including some crawlers and accessibility tools that run in no-script mode.