I’ve been auditing a ton of CSS lately and thought it would be neat to jot down how I’m going about doing that. I’m sure there are a million different ways to do this depending on the size and scale of your app and how your CSS works under the hood, so please take all this with a grain of salt.
First a few disclaimers: at Gusto, the company I work for today, our engineers and designers all write in Sass and use webpack to compile those files into CSS. Our production environment minifies all that code into a single CSS file. However, our CSS is made up of three separate domains. so I downloaded them all to my desktop because I wanted to test them individually.
Here’s what those files do:
manifest.css: a file that’s generated from all our Sass functions, mixins and contains all of our default HTML styles and utility classes.
components.css: a file that consists of our React components such as
Card.scss, etc. This and
manifest.cssboth come from our Component Library repo and are imported into our main app.
app.css: a collection of styles that override our components and manifest. Today, it exists in our main application repo.
After I downloaded everything, I threw them into an S3 bucket and ran them through CSS Stats. (I couldn’t find a command line tool that I liked, so I decided stuck with this tool.) The coolest thing about CSS Stats is that it provides a ton of clarity about the health and quality of a site’s CSS, and in turn, a design system. It does this by showing the number of unique
font-size and unique
background-color CSS declarations there are, as well as a specificity graph for that particular CSS file.
I wanted to better understand our
manifest.css file first. As I mentioned, this file contains all our utility classes (such as
c-salt-500) as well as our normalize and reset CSS files, so it’s pretty foundational for everything else. I started digging through the results:
There are some obvious issues here, like the fact that there are 101 unique colors and 115 unique background colors. Why is this a big deal? Well, it’s a little striking to me because our team had already made a collection of Sass functions to output a very specific number of colors. In our Figma UI Kit and
variables_color.scss (which gets compiled into our manifest file) we declare a total of 68 unique colors:
So, where are all these extra colors coming from? Well, I assume that they’re coming from Bootstrap. Back when we started building the application, we hastily built on top of Bootstrap’s styles without refactoring things as we went. There was a certain moment when this started to hurt as we found visual inconsistencies across our application and hundreds of lines of code being written that simply overrode Bootstrap. In a rather gallant CSS refactor, I removed Bootstrap’s CSS from our main application and archived it inside
manifest.css, waiting for the day when we could return to it and refactor it all.
These extra colors are likely come from that old Bootstrap file, but it’s probably worth investigating some more. Anyway, the real issue with this for me is that my understanding of the design system is different from what’s in the front-end. That’s a big problem! If my understanding of the design system is different from how the CSS works, then there’s enormous potential for engineers and designers to pick up on the wrong patterns and for confusion to disseminate across our organization. Think about the extra bloat and lack of maintainability, not to mention other implications.
I was reading Who Are Design Systems For? by Matthew Ström and perked up when he quotes a talk by Julie Ann-Horvath where she’s noted as saying, “a design system doesn’t exist until it’s in production.” Following the logic, it’s clear the design system I thought we had didn’t actually exist.
Going back to
manifest.css though: the specificity graph for this file should be perfectly gradual and yet there are some clear spikes that show there’s probably a bit more CSS that needs to be refactored in there:
Anyway, next up is our
components.css. Remember that’s the file that our styles for our components come from so I thought beforehand that it’s bound to be a little messier than our manifest file. Throwing it into CSS Stats returns the following:
CSS-Stats shows some of the same problems — like too many font sizes (what the heck is going on with that giant font size anyway?) — but there are also way too many custom colors and background-colors. I already had a hunch about what the biggest issue with this CSS file was before I started and I don’t think the problem is not shown in this data here at all.
Let me explain.
A large number of our components used to be Bootstrap files of one kind or another. Take our
Accordion.jsx React component, for instance. That imports an
accordion.css file which is then compiled with all the other component’s CSS into a
components.css file. The problem with this is that some
Accordion styles affect a lot more than just that component. CSS from this this file bleeds into other patterns and classes that aren’t tied to just one component. It’s sort of like a poison in our system and that impacts our team because it makes it difficult to reliably make changes to a single component. It also leads to a very fragile codebase.
So I guess what I’m saying here is that tools like CSS Stats are wondrous things to help us check core vital signs for CSS health, but I don’t think they’ll ever really capture the full picture.
Anyway, next up is the
This is the “monolith” — the codebase that our design systems team is currently trying to better understand and hopefully refactor into a series of flexible and maintainable React components that others can reuse again and again.
What worries me about this codebase is the specificity of it all what happens when something changes in the
manifest.css or in our
components.css? Will those styles be overridden in the monolith? What will happen to the nice and tidy component styles that we import into a new project?
Subsequently, I don’t know where I stole this, but I’ve been saying it an awful lot lately — you should always be able to predict what your CSS is going to do, whether that’s a single line of code or a giant codebase of intermingled styles. That’s what design systems are all about — designing and building predictable interfaces for the future. And if our compiled CSS has all these unpredictable and unknowable parts to it, then we need to gather everyone together to fix it.
Anywho, I threw some of the data into a Dropbox Paper doc after all this to make sure we start tackling these issues and see gradual improvements over time. That looks something like this today:
How have you gone about auditing your CSS? Does your team code review CSS? Are there any tricks and tips you’d recommend? Leave a comment below!
This is exactly why I’m building all the tools around Project Wallace! It’s an extra layer on top of CSS stats, so you can keep track of the history of your CSS stats. Check out https://www.projectwallace.com for that, maybe even the CSS Tricks site I’ve been tracking for a while: https://www.projectwallace.com/teamwallace/css-tricks
Also, you mentioned the lack of a CLI tool you liked. Perhaps wallace-cli will do the job? https://github.com/bartveneman/wallace-cli.
In addition to having StyleLint for linting our partials, we use gromit-cli (https://github.com/bartveneman/gromit-cli) to validate that our final CSS doesn’t output any more colors than specified in our colors settings.
Finally, that last section with a table to have a quick overview of seems like a great idea. I’d love to build such a feature in Project Wallace.
Oh my….this project is awesome. Thank you! Definitely going to start tracking my projects with Wallace.
With all that CSS I would think it would make sense to break it up into a base file and load in module by module as needed instead of asking a visitor to download it all at once up front. Maybe you’re already doing that and these files are combined just for analysis.
The only way I have managed to avoid cross dependencies has been by using CSS-in-JS, the naming convention .viewName-Component[-componentPart][–modifier] or by using shadow DOM. Shadow DOM is still unfeasible to use. The BEM-like convention is verbose and doesn’t support versioning (multiple versions of a view in the same app).
So.. CSS-in-JS any day.
Great article Robin!
I used to work on the Yelp core front end team and I guess the stats there tell a similar story https://cssstats.com/stats?url=https%3A%2F%2Fwww.yelp.com%2Fbiz%2Fgary-danko-san-francisco&ua=Browser%20Default
I got the opportunity to work on other code bases (smaller or similar size) and the numbers didn’t look much better.
There are for sure some scaling variables (no. of engineers making changes etc) and the age of a codebase to take into account, but I think the stack plays a huge role into this.
Classic preprocessor + naming convention + design system (that often is mock ups rather than the source of truth/code) don’t scale imho.
I think that a “design tokens” system + components written with atomic CSS with flat specificity can solve many of the issues highlighted by the stats. We just need good tooling to maybe get those benefits by default.
This tool could be a CSS in JS library, but it doesn’t necessarily have to be that.
Eg. I started an experiment that is a way in between CSS Modules and CSS Blocks – I called it Deterministic Style Sheets (DSS). I’ll leave some links here and welcome any feedback you might have:
An example of design tokens https://twitter.com/giuseppegurgone/status/1019431376020561921
More on DSS https://survivejs.com/blog/dss-interview/
I’m actually working on css colors audit currently at blacklane.com and was missing the tool which would show me all the colors in my src/ folder.
So I wrote one: https://www.npmjs.com/package/colors-in
Maybe, this can be helpful!