📝 CSS-Tricks Special Edition: CSS in Web Performance

View this newsletter on the web.

Surprise surprise, we write about CSS quite a bit around here. It’s always interesting to think about where it sits in the pantheon of web performance. Generally, the biggest thing that is talked about with web performance is stuff like how much JavaScript is on the page (too much!) and how large media has gotten (too big!). But consider that very first paint of an uncached page. If you’ve followed industry advice and are deferring all/most of your JavaScript, you’ll probably get that paint before it’s even all downloaded. And media (like images) don’t hold up the first paint, to begin with. But you know what does hold it up? CSS.

CSS is Render Blocking

I like that CSS is what you’d call “render blocking”. If it wasn’t, the web would either be very jerky-looking all the time, with extreme “Flashes of Unstyled Content” (FOUC). But knowing that it is, doesn’t it make sense that we try to load as little of it as we can, especially upfront?

Lots of tools attempt to help with this. This is the whole mission of what we call “Critical CSS”, where the only CSS that loads is that which is needed for that very first paint, and the rest is deferred. Even to the point where ideally that little chunk of CSS is in the first network packet that comes across the wire! This very site is a WordPress site, and uses the free plugin Jetpack Boost to do it, which is impressive.

Shortcut is project management built specifically for software teams and it’s fast, intuitive, flexible, and powerful.

Built by engineers for engineers.

Get Started for Free

Do we make the CSS async? Or can we make the JavaScript ignore that CSS is blocking?

Stoyan Stefanov has a couple of articles recently about this CSS blocking behavior and things that can be done about it. In Combat CSS blockage with this one weird little trick he talks about the fact that CSS blocks JavaScript execution and how that’s extra bad in today’s world of heavily JavaScript powered sites:

Blocking JS execution is bad. In today’s JS-rich apps a delay like this is a life/death situation. If it was a “progressive enhancement” JS that makes a readable article even more enjoyable, fine. But an app not showing your email message you’ve beeen eagerly awaiting just because of a network packet hiccup in CSS… that sucks.

So what can we consider here? Well what if we made CSS “asynchronous”? Meaning it would stop blocking any other resources. Turns out this is trivially easy, as Scott Jehl noted in The Simplest Way to Load CSS Asynchronously:

<link rel="stylesheet" href="/path/to/my.css" media="print" onload="this.media='all'">

But doing this nearly guarantees FOUC, so if you’re doing it, you’re probably loading some blocking critical CSS up front, and using this for the “rest” of your CSS. Stoyan’s article actually looked at another approach. That is, making some JavaScript run without waiting for CSS, even if you don’t change the HTML syntax for loading the CSS at all. It’s not async or defer on the <script>, those do nothing unfortunately in this case. It’s a Data URL! If you load your JavaScript like this:

<script async src="data:text/javascript,log%5B%27inline%20HEAD%20script%27%5D%20%3D%20%2Bnew%20Date%20-%20start%3B"> </script>

It will execute as soon as the browser sees it, not waiting for the CSS to load. Weird trick. Probably not trivially easy to take advantage of. But good to know when you absolutely need it.

Using Less CSS

Knowing that CSS is a blocking resource has led people down other performance paths. Stoyan wrote another article this year introducing a quick little bookmarklet to test not loading some stylesheets at all. Certainly, yes, if you have junk stylesheets that do nothing for you, those need to get gone. But what about just loading less CSS in general? Certainly, you’ll want to minify it and serve it as compressed as possible, that helps. You’ll want to make sure it’s cached so subsequent page loads pluck it out of cache. Those are just classics of web performance work. Removing unused CSS is also another path here, albeit an extremely tricky one.

Another player here is this whole world of Atomic CSS. I’ve never hide the fact that I don’t really like the idea of styling elements through HTML classes (or other attributes). But that’s just me, the proof is out there that lots of web developers really like it. I’m talking about the approach of Tailwind or Windi CSS, and before them, something like Tachyons, and before that, something like Atomizer. They all share this concept where you make styling decisions with HTML classes, and with most of them, minimal CSS can be generated that covers all the things that those HTML classes have asked for. The end result is CSS that is almost always far smaller than CSS you’d hand author.

Performance isn’t even one of the things these frameworks tout as a benefit of them. It’s usually more about “speed” or “maintainability”. I’m not convinced on those terms, but smaller CSS can’t really be argued against and has very clear benefits.

Shortcut is the ideal solution for task management, bug tracking, iteration planning, and reporting.

Perfect for growing engineering teams – ship quality software, faster.

Get Started for Free

What about CSS in JS?

You might be wondering how all this CSS-in-JS stuff factors into CSS web performance. Imagine a major player like styled-components where you aren’t authoring CSS in a .css file, but instead your JavaScript component files have chunks of CSS-like code in them to do the styling.

It’s fascinating to consider the tradeoffs. JavaScript is definitely a slower thing to load and execute than CSS is, so that’s going to make things slower off the bat (more JavaScript). But right away you have to factor in that we may not be loading any .css files at all! That provides some return on speed right out of the gate, totally unblocking the browser from getting to those JavaScript jobs. And there are other little potential speed benefits, like the fact that code splitting is common in JavaScript-rendered sites meaning we aren’t loading styles for components that aren’t in use. Plus “unused” CSS becomes almost a non-issue in CSS-in-JS.

You’d think all this might come out in the wash, but the data that I’ve seen suggests that CSS-in-JS is almost certainly a slower approach. Tomas Pustelnik:

TLDR: Don’t use runtime CSS-in-JS if you care about the load performance of your site. Simply less JS = Faster Site.

…

I believe we (developers) should think more about the impact of the tools we choose for our projects. The next time I will start a new project, I will not use runtime CSS-in-JS anymore. I will either use good old CSS or use some build-time CSS-in-JS alternative to get my styles out of JS bundles.

But note that Tomas was testing styled-components against another library that I would think many people still think of as a CSS-in-JS library: Linaria. In fact, it looks quite a bit like styled-components looks when authoring. But the major difference is that Linaria spits out *.css files that you load in the traditional way. They call that “zero-runtime” in CSS-in-JS circles.

It’s taken for granted at this point that when it comes to CSS, the best performance is… CSS. Just as little of it as is reasonably possible.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s