There are a lot of tools that aim to help you remove “unused CSS” from your project. Never a week goes by that I don’t see a tool for this being shared or promoted. It must strike some kind of perfect chord for some developers. I care about performance, and I know that reducing file sizes is good for performance. Indeed, it is. I bet we have CSS that is unused in our stylesheets, if we removed that, that’s a performance win. Yep, it would be. We should automate that. Ehhhhhh, I’m not so sure.
There are major performance tooling players that play up this idea, like Lighthouse and how it gives you CSS and JS “Coverage”, which will surely tell you that you’re shipping code you don’t need to be.
The tools that claim to help you with unused CSS have to perform analysis to be able to tell you what is unused and what isn’t.
Here’s one way to do that analysis. Render a page of your site and get the complete DOM. Then get the complete CSSOM as well, which can give you an array of all the selectors in your CSS. Loop over those selectors and do a querySelector
in the DOM and see if it matches anything. If it doesn’t, that CSS selector is unused.
Clever, right?!
I think so. But that analysis paints a rather limited picture.
Say that analysis runs two seconds after the page is complete, but there is some JavaScript that runs and injects a modal after five seconds (ughghk, I know). The analysis would have missed the HTML in that modal, which likely has styles, and thus would have incorrectly reported those styles as unused.
So, timing is one factor. Hopefully, this analysis tool has some way to configure multiple timings.
We’re also only looking at one page so far. Of course, a site might have tens, hundreds, or thousands of pages. To be entirely sure about unused styles, looking at all of them is the most sure-fire bet.
Multiple pages is another factor. Hopefully, an analysis tool has a way to look at as many pages as you tell it to. Perhaps it can look at a sitemap?
Remember the timing thing? We might think of timing as one generic form of state. There are countless other things that could be state related. Is the user logged in or not? What plan are they on? Is their credit card expired thus showing some kind of special message? Do situational things like time/date/geolocation change state? What about real-time data? Stuff from an API?
Application-level state is clearly a big factor. Hopefully, this analysis tool can trigger/set all possible combinations of state.
There is interactive state as well. What about modals that come up because something is clicked? What is the active tab? Is this menu open or closed? What scroll position are they at? There are infinite permutations of this. Imagine a warning bar that shows up seven seconds after the user logs in to warn user about their expired credit card which contains a custom styled select menu which can be in an open or closed state, but only on the user settings page.
It seems unlikely that this analysis tool can handle all those possibilities. Even with loads of configuration, mock state, and integration testing, it couldn’t cover the near-infinite possible permutations of all this.
And yet, I don’t think these tools are useless — they are just…tools. Their use can actually be a positive step toward better code. Their use says OK, I admit it, I’m a little afraid our CSS. You could use this tool to get a broad picture of what your unused CSS might be, then combine that with your own knowledge of your CSS code base to make more informed decisions. Or take another technological step and do something like add a background image to those unused selectors and check server logs to see if they get hit.
It should be said that this whole idea of unused CSS is a part of the CSS-in-JS saga that our industry is going through. If all your styles are written as part of components, there kinda is no unused CSS. Either the component gets used and the styles come with it, or it doesn’t. If you’re particularly sensitive about the danger of unused CSS, that alone might sway you toward a CSS-in-JS tool.
It also should be said that this DOM and CSSOM analysis technique is only one possible way of checking for unused styles. If you had some kind of fancy tooling that could analyze all of your templates, styles, and scripts, presumably that could determine unused styles as well. We talk about that in the recent ShopTalk Show episode with Chris Eppstein.
My personal experience, performance bloat is called by all the third party resources a site pulls in (e.g. this one calls 21 according to Privacy Badger). If CSS is static and has an appropriate cache life, it is only possibly a factor when it isn’t already cached. If you include the timestamp of the last modification date of the CSS file when including it (use mod_rewrite to serve the actual file) then you can send it with a header that says cache it for a year but any modifications to are immediately served because the timestamp changed.
Anyway of course CSS that you are never going to use again should be removed, but automated removal because it isn’t currently being used – bad idea.
Upvote +1
Your thought is what I was afraid when using tools like Lighthouse. My conclusion, we cannot detect unused css at runtime, the only way to detect is beforehand. If you use module bundler tools like Webpack, I wrote a plugin which help us to achieve what we need
https://github.com/MQuy/es6-css-loader
https://github.com/MQuy/webpack-deadcode-plugin
DM, if you want to discuss more details
Component based assets, like mentioned, seem to me to be the way forward – maybe not so much in the CSS-in-JS arena but in all facets of development?
This kind of goes hand in had with Critical and Deferred styles, where you have a framework stylesheet that is imperative for the site to display but all others are only brought in as needed and then “flushed” away after expiration.
Once an asset is loaded into the cache it’s not like it has to be downloaded again and again, I personally feel that downloading only what is required for that page at that time is the way forward while we still have issues like LiFi and 3.14159265358979323846264338327950288419716939937510582097494 G cellular networks! ;-)
Good article on all points though Chris, thanks again for putting so eloquently what I’ve been thinking! #h5yr
This is one reason that there’s a trend towards compiler-frameworks that truly can remove unused CSS safely. OptiCSS (the companion to CSS Blocks, mentioned in the article) is an example of tooling that can do so by virtue of knowing everything about both your templates and your CSS.
Svelte (disclaimer: I’m the lead maintainer) is another. Because it analyses a component’s CSS (parsed with css-tree in the context of the component’s markup, it’s able to eliminate unused styles from the output .css file with no false positives and only rare false negatives (i.e. it may retain some styles in certain edge cases, but it will never remove styles incorrectly). Here’s a demo, to give you an idea of how it works.
Looks like Rich pasted a local machine link instead for the demo. I believe this is the correct link:
https://svelte.technology/repl?version=2.7.2&gist=510aaa876132392e69caab6e91f91cf2
Gah, I’m an idiot — thanks Louis, that is the correct URL!
I think a good way to keep unused CSS from piling up is to create distinct stylesheets for every component and view, for when a markup file is removed from the project, the related CSS is also removed.
I usually accomplish this by creating a filesystem structure under the styles directory that resembles that of the views directory and every view file gets a corresponding LESS file. Then everything is collapsed into a few, entry-point stylesheets, where everything gets imported.
Then it’s just a matter of removing the stylesheets that don’t have a view anymore.
Here, here!
Awesome… I love how these “useful tools” remove font or background colour when it’s set to default black on white, right? Only there’s no such thing as default CSS colours. It’s all browser, OS and user settings specific so you can never ever set one without the other or you’ll end up with black on black or white on white and similar unreadable combinations. Always set both colours for all elements, it’s not unused CSS!
And yes, my OS is set up to white on black and every major browser picks it up out of the box.
Imagine you are writing CSS for consistent typography and then this tool removes your
blockquote
styles because your current page does not have quotes in the article.