First off, it's only fair that I tell you up front that I strongly dislike the concept of critical css. I get the page speed impact, but if you've been a web developer for any time at all, critical css feels like a step backward. We (I'm speaking very loosely here) created lots of great tools for combining, minifying, and serving css, and now Google is telling us we should start inlining css again because it's theoretically a little faster. Sorry . . . that annoys me just a little (and by a little, I mean I hate it).

That's why I like purge css so much. It's like the good version of critical css. Don't jump through crazy gymnastic hoops to inline the css your above-the-fold content needs. Just make your bundle smaller by only including the things you actually use.

But maybe I'm getting ahead of myself. Let's look quickly at what each of these things are and how they work.

Critical CSS

Critical css, as I mentioned, is a page speed optimization technique proposed (forced) by Google Pagespeed wherein you inline the styles your webpage needs to render the complete viewport (called "above-the-fold" content) so that a user has a mostly complete and maybe semi-functional view of your page almost immediately. The name "critical css" is derived from the concept of the critical path: find the css that's needed immediately for the page to look right and make those specific stylings as fast as possible. And the fastest way to load/parse CSS is inline because the browser doesn't need to make an additional network request. So as an example, a page (or the top of a page) before critical css inlining might look like this:

<!DOCTYPE html>  
<html>  
  <head>
    <meta name="stuff" content="value">
    <title>Nifty Page</title>
    <link rel="stylesheet" href="/my-stylesheet.css">
  </head>
  <body>
    <div>Some content that takes up the whole screen.</div>
  </body>
</html>  

After inlining css, that link element is replaced by a big chunk of css:

<!DOCTYPE html>  
<html>  
  <head>
    <meta name="stuff" content="value">
    <title>Nifty Page</title>
    <style>
      div {
    background-color: red;
        /* other styles */
      }
    </style>
  </head>
  <body>
    <div>Some content that takes up the whole screen.</div>
  </body>
</html>  

Usually that inlined chunk of css is also minified, as that makes the total html payload sent to the browser much smaller.

There are tools for generating critical css, probably most notable penthouse.js. I've only used penthouse, so I can only speak to that tool specifically; it actually uses puppeteer, a headless Chrome runner, to open your actual page and parse the css necessary to render the above-the-fold content. There's a lot of options for controlling how this works. If you're interested in a more thorough description of how one might use this, I've written about it before.

Purge CSS

PurgeCSS is a tool that examines the classes (and presumably elements) your actual html employs and removes everything else, so you end up with a css bundle with only the selectors you're actually using. There's not much more to say because at that point, it's really not that different than a normal css approach: you serve the bundle however you serve css. It's just a smaller payload.

It's probably worth noting at this point that there's nothing that keeps you from combining these approaches. Once you generate a purged css bundle, you could inline it using similar strategies to those used for critical css or even use it to generate above-the-fold css and only inline that.

Advantages and disadvantages

Both of these tools have their headaches when you're learning them and getting them set up and integrated into the rest of your build process. Installing and using penthouse is probably slightly more involved because it involves downloading platform specific binaries. Integrating purge css into an existing build flow (like webpack) is pretty simple once you get past the initial configuration fatigue (not sure if this is a real term but I'm using it to mean that feeling you get when you first open the docs for a tool and feel overwhelmed by the number of options). In my experience, they're both fairly effective at doing what they claim they should, but penthouse is a bit more of a black box. I've generated critical css a couple times in a row without changing any styling or layouts and had git still report a difference, so it seems like penthouse can be a bit non-deterministic. To be fair, it's a pretty complicated thing it's trying to do, but it still seems like the same input should yield the same output. Generating critical css, at least with penthouse, is also very slow because it has to spin up a web browser to do its parsing. It also requires a url to consume (it's a web browser after all), which could be a limiting factor depending on your set up.

Purge css is obviously significantly less sophisticated. It's not looking at an actual page to determine css usage, so the chances for false negatives and positives are higher. For instance, any classes applied by scripts aren't going to be bundled, nor are any assigned in a template via a templating processor such as mustache. There are plenty of ways around this; purgecss has a whitelist where you can supply classes that should always be included, and you can tell it to scan other files besides html as well. But probably the best solution is to just not use a lot of dynamic styles, which is significantly clearer in terms of usage and self-documentation anyway. Obviously, that can't always be avoided though.

One big downside of critical css that you don't get with purge css is duplicate styles cluttering up the styles panel in your developer console. If your critical css styles come from a larger css bundle that you also load after the critical css, then many of your styles are going to appear in the styles panel twice. This isn't the end of the world, I guess, but I find it annoying when debugging styles. There are ways around this (the method we use is described in the aforementioned post about our critical css setup). I think Google foolishly believes you would just abstract out the styles necessary for critical css and load them first, and then load a separate bundle that no longer contains those styles, but come on. Ain't nobody got time for that! Oh and of course, if you do load critical styles, followed by the bundle they came from, your loading an, ultimately, larger css payload because you're duplicating things.

So for simplicity of use with compromising bundle size, I strongly recommend purge css (no surprise if you read the very first sentence). Remember (regardless of what Google thinks) that PageSpeed insights are only suggestions about things that may improve page performance. But page performance is a vastly complicated arena of optimization, and there are probably bigger and faster wins for you team that using critical css. Not too mention, if you serve any ads at all in above the fold content, you might as well forget about having a good PageSpeed score!