Loading just-in-time CSS in the Body with WordPress

A motorcyclist speeding on a highway
Credit Lalo Hernandez

I was reading Harry Roberts’ exhaustive CSS Wizardry post about CSS and Network Performance and he points the way toward a possible future of CSS: not just in the head, but with stylesheets linked throughout the page as needed. A bit from his example:

<!DOCTYPE html>
<html>
<head>

  <link rel="stylesheet" href="core.css" />

</head>
<body>

  <link rel="stylesheet" href="site-header.css" />
  <header class="site-header">

    <link rel="stylesheet" href="site-nav.css" />
    <nav class="site-nav">...</nav>

I think you get the idea: modern and future browsers will allow us to load CSS directly before the elements they style and render the page progressively, instead of having to traverse a single full stylesheet at the top of the page before beginning the render. This potentially improves the user experience and site speed dramatically. (Here’s more, from Jake Archibald’s 2016 article.)

But how can we do this in WordPress?

It’s a bit tricky. Proper WordPress stylesheet inclusion means using wp_enqueue_scripts, a hook which sends stylesheets only to wp_head after putting them into the system for access via plugins like Autoptimize and so on.

We need two things:
– a progressive place in the theme to load the stylesheet to
– a way to send stylesheets to non-head locations

We can take care of the first with action hooks. Themes like Genesis are full of these, and you can pepper your theme with them easily:

<?php do_action( 'before_footer' ); ?>
<footer>
  ...

All you need to do is declare the hook in the right spot (perhaps footer.php) with the do_action() function. Your footer is now ready for styling.

For the second part, we’ll head over to functions.php, where instead of wp_enqueue_style, we can rely on an older WordPress function, wp_print_styles:

function enqueue_theme_stylesheets() {
  wp_register_style( 'footer', get_template_directory_uri() . '/css/footer.css );
}
add_action( 'wp_enqueue_scripts', 'enqueue_theme_stylesheets' );

function footer_styles() {
  wp_print_styles( 'footer' );
}
add_action( 'before_footer', 'footer_styles' );

We still need to register our stylesheet to get it into the WordPress queueing system, but then we can just send it to the hook to print.

And we can do better: WordPress’ enqueue and registration functions allow us to set media queries (which Harry covers for non-WP use as well), so we can load the minimum amount of code for mobile and scale our way up (or be even pickier).

function enqueue_theme_stylesheets() {
  wp_register_style( 'footer', get_template_directory_uri() . '/css/footer.css );
  wp_register_style( 'footer-800', get_template_directory_uri() . '/css/footer-800.css', array(), false, 'all and (min-width: 800px)' );
}
add_action( 'wp_enqueue_scripts', 'enqueue_theme_stylesheets' );

function footer_styles() {
  wp_print_styles( 'footer' );
  wp_print_styles( 'footer-800' );
}
add_action( 'before_footer', 'footer_styles' );

(We need to include the array() and false defaults above to get over to the fifth parameter, $media, where we can add our own media query.)

And on and on until you have a million tiny stylesheets in your CSS folder.

Of course, wp_enqueue_scripts has been the preferred way to load stylesheets since 2011—at the time, there was thought to be a conflict with wp_print_styles() with WordPress 3.3 that would send the stylesheet into the admin side. I don’t know if any such bugs or other concerns exist in WordPress 5.0, and would be interested to hear some feedback.

One other downside is that plugins such as Autoptimize will not respect your hook locations—if you are using it to minify and concatenate your CSS, it will grab the registered stylesheet and concatenate it with all the rest in the head as usual. So the good news is this method keeps your stylesheets within the WordPress flow, but the bad news is (unless there are alternatives I’m not aware of) you’ll have to minify your files before handing them off to WordPress. Perhaps this is something for a future Autoptimize update…

I’ve done brief testing with this and it works—I have yet to break down Rawkblog’s CSS this way to see what the performance benefits might look like. Please let me know if any of this makes it onto your site and how it works for you.

Nathan Rice has also written about how we might do this with CSS just for Gutenberg blocks, using a similar method, but perhaps there’s no reason not to do it for all our styles, everywhere.