Oh No! Our Stylesheet Only Grows and Grows and Grows! (The Append-Only Stylesheet Problem)

Avatar of Chris Coyier
Chris Coyier on (Updated on )

📣 Freelancers, Developers, and Part-Time Agency Owners: Kickstart Your Own Digital Agency with UACADEMY Launch by UGURUS 📣

This is a real worry these days. I’ve heard it from lots of lots of developers. The years tick by on their projects, and all they ever seem to do is add to their CSS, never remove. It’s not just a feeling, I’ve talked with companies before that track hard data on this. Over five years of tracking the size of their stylesheet, and all it’s ever done is tick upwards in size.

This could be considered problematic for several reasons:

  1. Files getting bigger is worse for performance
  2. The developers are afraid of the CSS

#2 being, in my opinion, a much bigger deal than #1. The overall file size of CSS is likely to be fairly small compared to things like image assets and even the JavaScript payload these days. Fancy tooling and the world’s internet speed ever-lurching faster will probably make #1 not that big of a deal.

But being afraid of your own styles is a bigger problem.

“Afraid” isn’t usually how this problem is talked about, but I think that’s what it amounts to. It’s often talked about in terms of how the global nature of CSS is problematic or that the middle “S” in “CSS” is the only one worth saving.

“Unusued CSS”

Part of this story could certainly be about deleting CSS that is determined to be “unused” in a project. I know there is incredible demand for this kind of tooling. I feel like there are some developers damn near frothing at the mouth to blast their CSS through some kind of fancy tool to strip away anything unneeded.

This worries me just a little bit. It feels like saying: “Yes, I am afraid of our stylesheets. I don’t want to understand them, I just want something to fix them for me.”

Here’s how one company I heard from was doing it:

  1. They injected a script onto the page for some subset of users.
  2. The script would look at the CSSOM and find every single selector in the CSS for that page.
  3. It would also run a querySelectorAll(“*”) and find every single DOM node on that page.
  4. It would compare those two sets and find all selectors that seemed to be unused.
  5. In order to get the best results, it would fire this script after a random amount of seconds, on a random set of users, in a random set of conditions. Even with this, it needed a lot of data over a long period of time.
  6. After that had run for long enough, there was a set of CSS selectors that seemed likely to be unused.
  7. To be sure, unique background images were applied to all those selectors.
  8. After applying those and waiting for another length of time, the server logs were checked to make sure those images were never accessed. If they were, that selector was used, and would have to stay.
    Ultimately, the unused selectors could safely be deleted from the CSS.

Whew! That’s an awful lot of work to remove some CSS.

But as you can imagine, it’s fairly safe. Imagine just checking one page’s CSS coverage. You’ll definitely find a bunch of unused CSS. One page, in one specific state, is not representative of your entire website.

Websites have multiple pages. JavaScript runs on them affecting the HTML. Users may log into them, displaying things in a different state. Dynamic things happen on websites. In order to really know what CSS is used, you’d have to test every possible page in every possible interactive permutation, which is a tall order. I’m sure you can imagine some CSS that applies only to a select menu for a logged in user with an expired subscription who has logged in to update a credit card which happens in a modal window with a form that displays a certain way because the card is American Express.

All that said, there are tools that purport to help you find unused CSS

Chrome has a “Coverage” panel (in Canary as I write) that can tell you what of your CSS is used:

It’s pretty nice in how you can hit the Record button, click around and do a bunch of stuff (even changing pages) and it will continue analyzing how much of the CSS gets used. Then you can see what is used or not with the red or green bars next to the CSS.

It suffers from the same problems that I described, in how you just clickin’ around isn’t enough to guarantee coverage. You’re very likely to miss edge cases, and if you make choices on CSS to delete based on your incomplete testing, you’ll be causing yourself problems.

There are other tools that take a stab at helping remove unused CSS, UnCSS probably being the most popular.

UnCSS does some smart stuff, like allowing you to list a whole array of URL’s to test together, provide media queries to apply, and runs JavaScript. Here’s their example configuration:

var uncss = require('uncss');

var files   = ['my', 'array', 'of', 'HTML', 'files', 'or', 'http://urls.com'],
    options = {
        ignore       : ['#added_at_runtime', /test\-[0-9]+/],
        media        : ['(min-width: 700px) handheld and (orientation: landscape)'],
        csspath      : '../public/css/',
        raw          : 'h1 { color: green }',
        stylesheets  : ['lib/bootstrap/dist/css/bootstrap.css', 'src/public/css/main.css'],
        ignoreSheets : [/fonts.googleapis/],
        timeout      : 1000,
        htmlroot     : 'public',
        report       : false,
        uncssrc      : '.uncssrc'
    };

uncss(files, options, function (error, output) {
    console.log(output);
});

I’d still worry that this would be difficult to configure (and keep configured) such that it provides 100% test coverage. Everytime you wrote a new CSS rule, you’d need to make sure it didn’t trigger a false positive here. That is, presuming you actually use this tool to delete CSS.

Not to mention that, while it does execute JavaScript, it’s not simulating interactions.

Another approach: frameworks

Imagine you used a CSS framework that provided every practical bit of styling you’d want to apply. Rather than writing CSS, you’d apply the classes that the framework applied to do what you needed to do. You’ve now tied your HTML classes pretty heavily toward this framework, but you’ve solved the growing CSS problem. Over the years, your CSS will remain flat.

I’m no expert in Tachyons, but that’s what it seems like to me. After you get used to using it, you get pretty speedy at coding up what you need with the side benefit of this rarely-changing static CSS file that nobody is afraid of.

This falls into a category that has come to be known as Atomic CSS, of which there are many players. A version of Atomic CSS is “programmatic”, in which you use special classes and a processing step generates the final CSS for you. The idea being that now you aren’t shipping a static framework of CSS, but only the very little bit of CSS you actually need.

John Polacek recently wrote about this. He’s finding that he both suffered from the growth of CSS problem, and found that Atomic CSS not only stopped the trend but reversed it:

Heck, even frameworks like Boostrap, Foundation, Materialize, or Bulma fit the bill here. The idea is that if you stick to the framework, you’ll never get to that undesirable state of being afraid of your own CSS.

CSS in JS

Managing styles in JavaScript (recommended reading) also can help with this problem. Styles that are entirely scoped to a particular module are, by nature, easy to delete. Don’t need the module, don’t need the styles.

Don’t worry too hard

I find all this stuff fascinating to watch and think about. As is often said: the web is a big place. All of us have unique circumstances. This problem affects some percentage of us, and dare I say, probably a fairly small percentage.

If you aren’t particularly worried about the size of your CSS and aren’t particularly scare of it, good! Neither am I, on most of my projects.

I just have a feeling (a prediction, really) that there is a modular future for CSS:

When we write styles, we will always make a choice. Is this a global style? Am I, on purpose, leaking this style across the entire site? Or, am I writing CSS that is specific to this component? CSS will be split in half between these two. Component-specific styles will be scoped and bundled with the component and used as needed.

What the prodominant tooling for that will be, I am unsure, if there even is one.