WordPress 5.0 and Gutenberg: A better way to handle CSS overload

As you may know, WordPress 5.0 introduced a new editor experience (for those that haven’t decided to explicitly disable it), and the response has been mostly “ok, cool.” from users, despite the controversial release schedule, and the decision to punt many accessibility and performance issues to subsequent patch releases.

And while I won’t try to overcompensate for the problems that have been pointed out, I will say that I mostly like the new editor experience, both from the perspective of a user (I’m writing this post in Gutenberg) and a developer.

And as a developer, I can’t help but notice that there is a pretty predictable problem staring down its nose at us.

The problem is front-end performance, and to be honest, it’s not really Gutenberg’s fault.

Some background

Gutenberg is a “block based” editor. Every piece of content in your post/entry is a block. Paragraphs, images, headings … all the normal stuff you’d put into entry content is a block.

And this opens up a world of opportunity for users to activate, as needed, new types of blocks.

Need a contact form on your contact page? There’s a block for that.

Need an image gallery? There’s a block for that.

Need a bunch of useful blocks in a single plugin? There’s a block library for that.

Now, in order to meet your needs in creating the kind of content you want, you may end up with several block plugins installed at once, in addition to the plugins you’re already using. Ignoring, for now, the risks of simply having too many plugins active, I want to focus on the bigger problem.

But first …

Waterfalls

When testing the performance of a website as it would appear to a user, developers do something called a waterfall analysis.

It’s basically a chart of every resource your site requires in order to load fully, and the time it took for each of those resources to download.

It’s a really good visual representation of page weight.

Waterfall for WordPress.org

I won’t explain waterfalls in too much detail; there are much better articles for that than this one. But to give a brief summary, each resource download your page uses is represented by a row. Each row has a colored line (far right) that indicates when the asset started downloading, and how long it took. Obviously, bigger files from slower servers are going to take longer, whereas small files from fast servers (like CDNs) will take less time.

In addition to helping you locate assets that are loading slowly (due to size or server speed), the waterfall shows you another useful metric: what resources are blocking other resources from downloading, and ultimately, blocking the page from rendering.

Although there are a few types of render blocking resources, for the purposes of this article, we’re going to focus on the most relevant one, stylesheets (CSS).

Render blocking stylesheets

Because there is currently no way to prevent stylesheets from blocking the render of a page (and for good reason, FOUC is no fun), we’ve basically just had to accept that our stylesheets were going to halt the render of our page until they’ve all downloaded.

Essentially, until all of the CSS is downloaded, nothing on the page will render.

So we invented little tricks to help minimize this issue, like minifying our CSS, combining all the CSS into a single stylesheet (where possible), and relying heavily on browser caching.

And when you have total control over your site, these aren’t terrible options.

But an extensible CMS will complicate things. Your theme loads some CSS, plugins load CSS, WordPress core load some CSS.

It’s not at all surprising to see a WordPress site loading 10+ stylesheets, and that’s just going to get worse with Gutenberg blocks needing their own CSS to render properly on the front end.

What’s worse, these stylesheets are being loaded regardless of whether they’re needed. If a page isn’t using a particular block, the block plugin will still likely load the CSS for that block.

But even if the block plugin were aware that the page was using the block, and only loaded the CSS when the block was being used on a page, we still have a problem! When you register and enqueue a stylesheet in WordPress (the Right Way), it will output to the <head>, as it is designed to do.

The problem is, now a stylesheet designed for a block that is used at the bottom of a blog post is blocking the render of the site header, or menu, or post title, etc.

Why should a block placed well below the fold affect the rendering of the site header?

Let’s Modularize

Conventional wisdom tells us that we need to serve as few CSS files as possible. Every HTTP request includes some network latency, and therefore reducing requests means less time until all our CSS is downloaded and the page is ready to render.

After all, if every CSS file is loaded in the <head>, and blocks the render of the entire page, then why wouldn’t you just combine all your CSS into a single file, a single HTTP request?

Note: although you could make the argument that modularizing your CSS files provides a more efficient cache busting opportunity, and HTTP2 allows for asynchronous CSS downloads, latency is still an issue so it’s still best for performance to combine CSS in production.

But, a couple of months ago, Jason Cohen shared and interesting link to the engineers at WP Engine. An excerpt:

due to a recent change in Chrome (version 69, I believe), and behaviour already present in Firefox and IE/Edge, <link rel="stylesheet" />s will only block the rendering of subsequent content, rather than the whole page.

CSS Wizardry

If all the stylesheets are linked in the <head>, this new information doesn’t really change much. It’s still blocking everything after the link … every visual element on your site.

But what if we split CSS into multiple files, each corresponding to a piece of content that needed styling. And instead of linking them all in the <head>, these CSS files would only be linked directly before the content they were designed to style was present.

It still blocks rendering, just like the traditional method, but because we’re linking the CSS file in the <body> right before the content it’s designed to style, we don’t block rendering of content before that point in the source code.

The practical upshot of this is that we’re now able to progressively render our pages, effectively drip-feeding styles to the page as they become available.

CSS Wizardry

Put another way, we’re drip feeding styles to the page as needed.

It also has the added benefit of strategically blocking the rendering of any content which doesn’t have corresponding CSS dowloaded yet. No FOUC.

Time to first render

So why is this a big deal? Because page speed (the actual time it takes for your full site to load) and perceived page speed (the time it takes for a user to think your site has loaded) are very different things.

And it turns out, users don’t care that much about how long your site actually takes to load. They’re not watching the waterfall, they’re watching the screen.

Anything we can do to reduce the time a user has to wait until they first start to see things loading on our page is a win for visitor retention. Users don’t see the footer when the page first loads. And with more and more traffic coming from mobile, there’s even less that is actually seen when a page is first accessed.

This presents a big opportunity! We can decrease the time to first render, and delay the rendering of elements further down the screen until after their related CSS has been loaded.

So, Blocks?

Admittedly, this is hard. WordPress is very specific about the way it wants you to load your CSS files.

Currently, you have the option to register a stylesheet with one function, and another function enqueues it (it also will do the job of registration, but you should really register and enqueue separately).

When you enqueue styles this way, every stylesheet link gets output to the <head>. Each stylesheet is registered with a unique handle, so the same stylesheet link doesn’t get linked more than once.

I’m not suggesting that we start linking our own stylesheets outside the WordPress way. But something does have to change.

What if you could register stylesheets the same way, but instead of enqueueing them to be output in the <head>, you could call a function manually that would output the stylesheet link (with any dependencies) immediately before a block. Once a link is rendered, the WordPress stylesheet manager could make sure it doesn’t get rendered again.

As it turns out, you can!

Here’s a quick proof of concept (and a gist). It’s using the CTA block from Atomic Blocks as the example, but obviously production code would look a bit different.

// this snippet requires PHP 5.3+
add_action( 'wp_enqueue_scripts', function() {
	wp_register_style( 'atomic-blocks/ab-cta', '/path/to/atomic-blocks/css/ab-cta.css', array(), '1.0.0' );
} );

add_filter( 'render_block', function( $block_content, $block ) {
	if ( 'atomic-blocks/ab-cta' === $block['blockName'] ) {
		ob_start();
		wp_print_styles( $block['blockName'] );
		$block_content = ob_get_clean() . $block_content;
	}

	return $block_content;
}, 10, 2 );

You essentially register the modular CSS as you normally would. Then, leveraging the render_block filter, you can output a stylesheet link “just in time”, so that the content gets CSS to style it before it gets rendered, but the CSS doesn’t block the rendering of anything before it in the source.

This is legitimately amazing. By using wp_register_style() and wp_print_styles(), we are working within the existing WordPress stylesheet loader, and we get all the benefits of doing so. But by not enqueueing the styles, and instead printing them directly where we want them, we can output the stylesheet links right before the block they style, and only if the block is actually used. Yes, we have to use output buffering, but that’s sometimes unavoidable, and in this case, it’s worth it.

Themes?

This method isn’t limited to blocks. Entire themes could be built this way. Instead of a master stylesheet that styles everything, use separate stylesheets for each section of your site and output the stylesheet link right before the section it styles.

Has it ever bothered you that WordPress themes load CSS for the comments section on pages with comments disabled, or on archives, or on the homepage? Yeah, me too.

By only linking a stylesheet when it’s needed, you only load CSS for sections that actually exist on the page. This can significantly decrease total page weight, in addition to the decreased time to first render mentioned before.

Plugins

Plugins have the exact same opportunity that themes and blocks do.

It’s really not complicated. Simply register your stylesheet as you normally would, then use wp_print_styles() to output the CSS as part of any rendering functionality your plugin uses. If your plugin provides a widget, then make sure the widget styles get output when the widget renders … don’t enqueue the stylesheet like you used to!

Gotchas

So what’s the catch?

Currently this method works in all major browsers except Safari. It doesn’t break or anything, it just treats each linked stylesheet as if it were in the <head>. That is, each linked stylesheet is render blocking for the entire page.

So basically, in Safari, we end up with the same outcome we’d have if we didn’t implement this method at all.

Sure, by using this method, you might sacrifice some performance in Safari (until they fix things) by not concatenating all your CSS into a single file, but by having a stylesheet for each major section or content type, and only linking it when that section or content type is actually in use, you also gain some performance by reducing your total page weight.

One possible issue is that on pages where we’re not actually reducing total page weight – a page that uses a lot of sections, blocks, etc. – we might technically be increasing the total pageload time by modularizing our stylesheet loading method. It’s possible that search engines like Google, who prioritize speed, might penalize you for choosing time to first render over time to full render. But in my opinion, this is unlikely.

Am I wrong?

the best way to get the right answer on the internet is not to ask a question; it’s to post the wrong answer.

Ward Cunningham, Cunningham’s Law

So, what did I miss? I want to kick off a discussion of this problem, the proposed solution, and also kick around other ideas.

I’d also love to see actual metrics for this method. I haven’t been able to do a lot of testing, so this post is pretty theoretical when it comes to the performance benefits I’m predicting.

If you want to discuss this on Twitter, you can find me at @nathanrice

12 Replies to “WordPress 5.0 and Gutenberg: A better way to handle CSS overload”

  1. Hi Nathan – the thing you got wrong is that most users thing Gutenberg is cool. On WordPress itself, 1600 users have given it 1 or 2 stare reviews, while only 600 have given it 4 or 5 star reviews.

    Users mostly find it terrible.

    I’ve been using the Internet for 30 years and Gutenberg is the worst interface I have ever seen. The block concept is insane. And yes, it has killed speed. Takes me twice as long to get a post up, random bits of functionality break. It never works the same way twice.

    1. I noticed some quirks while editing this post, but generally disagree with your assessment here. Which is fine, not everyone is going to agree on the topic of Gutenberg.

      I stand by my statement, though. I have access to data (support requests, install stats, etc.) that suggests that, in fact, the coming of Gutenberg was pretty non-eventful for most WordPress users.

  2. Interesting post. I will be moving into Guttenberg in February on my clients sites, so I will give you a link to my data on the experiment at that time.

  3. Hey Nathan,

    This is an interesting analysis, but from my non-WP experience in the past, I feel like having multiple CSS files and maintaining all of them separately isn’t worth it.
    Is it really so awful to have an extra 3kb worth of CSS code in the same file, considering that the file is later minified and gzipped?
    At least we can always point our customers in the same direction: style.css. You don’t have to worry about the order in which the files are loaded, each of them having the ability to overwrite a previously loaded file…

    Inline CSS is of course not a solution, but I feel that the idea of multiple CSS files is not one either.

    But it is good to try and think outside the box, only so can the world make progress πŸ™‚

    Cheers!

  4. Wow, that’s really interesting. Though part of me is cringing at not properly enqueuing – I have several cases where I want more control of styles, so I dequeue say a plugin’s CSS and just build my own into my theme. So I think ultimately WP needs a way to actually enqueue the CSS, but place it where you want – similar to how you can currently enqueue JS and set it to either be in the head tag or the footer.

    1. Perhaps so, but it’s important to know that using wp_print_styles() isn’t a hack. It’s actually the purpose of the function, and works perfectly well in the WP styles registration and output system.

  5. Fwiw WP Rig loads styles in-body exactly like what you outline here.

  6. Great post and helpful! Thanks for opening the door on this subject. I’m looking forward to using this this technique on a current project.

  7. Good analysis, people who say Gutenberg is slow should check this topic. Hope Gutenberg becomes more user-friendly in the upcoming updates.

  8. Interesting approach. However, we need to care about 2 other things:

    – Dependencies: it’s not as important as JS, but it affects the CSS order, which means CSS specificity and can cause unexpected results. Does the wp_print_style() function handle this issue well?

    – Duplication of styles. I think there should be a check before print styles, so each style is printed only once.

    1. – Dependencies: wp_print_styles() SHOULD handle this, yes.
      – Duplication: yes, this already exists, I believe.

  9. Sidebar:

    I have a tough time just getting all the buttons on a site similarly styled!

    Couldn’t we have a centralized CSS for all buttons that theme developers; plugin developers, widgets and forms would all be required to use?

    This would save me so much time!

Comments are closed.