A response to Managing CSS & JS in a HTTP/2 World
This article on getting great performance out of HTTP/2 would have excited me hugely before. Sane structure and tooling around SCSS that allows for the best loading benefits with HTTP/2 is definitely the future when your styles are separate from everything else.
I believed in separating styles, behavior, and structure for a long time. Problem is, after diving in with Styled Components from the CSS-in-JS movement, I’m not in that camp anymore. We should encapsulate styles, behavior, and structure together.
With React, we already intermingle behavior and structure to great effect. Is it the indefensibly best way all the time? Of course not, but it’s my favorite for the projects I work on.
The CSS-in-JS movement took this to next level: break the last separation. Keep style alongside behavior and structure.
Coming from that context, reading about the right way to manage CSS felt like watching exciting evolution for a dated paradigm. A cleaner design for a horse harness is a beautiful thing but doesn’t beat the engine.
Solving the problem of chunked CSS delivery in order to get performance benefits from HTTP/2 sounds to me like reinventing the wheel from what you’re probably already doing. If you’re writing a web app, aren’t you already deploying well-separated HTML/JS chunks for the best performance? Why rebuild that process in parallel for CSS?
The core problem comes down to managing CSS inclusion/exclusion from the project through a human process. Let’s look at some consequences from keeping humans in the loop.
Here are some fallacies with the proposed setup:
The Structure Works As Long As Devs Make It Work
The great file structure proposed in the article would help keep things organized. But who’s going to be responsible for keeping it that way? What if you simply didn’t need a process around maintenance because it took care of itself?
A problem that hasn’t been flatly solved by CSS tooling is code creep: when encapsulation is broken and styles are lost track of, living in perpetuity in the source code. The result is endlessly growing source code and convoluted strategies for keeping the delivered site in check (if you even try to mitigate the problem).
Sprinkling well-scoped CSS files into the HTML via <body> link tags sounds like a good solution. But who is maintaining the SCSS source files? Is your process strong enough to ensure that CSS creep will be negligible for the life of the project?
The security community revolves around the belief that removing humans from a process, and therefore removing the need to trust those humans, improves the overall security of a system by removing the human risk. Likewise, a style framework that removes the burden on human process to keep things tidy by nature won’t have CSS creep problems.
We Can Just Strip Unused CSS
If you have significant CSS baggage you might integrate tooling to strip dead CSS, but those tools have consequences too. If your site is dynamic then you have to ensure the stripper doesn’t strip the wrong code. Can you adequately maintain a well-scoped whitelist to make sure this doesn’t happen?
In contrast, with a CSS-in-JS setup unused code that makes it into the bundle gets stripped without consequence. The process is called dead code elimination; it’s a major selling point of Rollup and Webpack 2. No worrying about whitelists here.
The Argument for CSS-in-JS Instead
When was the last time you saw a project where the CSS had at least some untouchable baggage? Code that no one really knows what it impacts, and whose removal could have unintentional ripple effects? CSS and SCSS requires proactive management from the developers to keep it sane, and the moment that slips, it very quickly becomes easier to just never remove CSS.
With a framework like React, you already have your “HTML” and business logic in the same place. So it cleans itself up when you remove the React logic. If you add styles to the mix then you gain that automatic organization for styling too.
At the same time you can retain the ability to write real CSS syntax, but with the added benefit of dynamic JS thrown in there. What if the idea of a <Provider> from the React world could drive your styling as well? Uniting styles with layout and business logic drives unexpected new synergies and a cleaner project.
If you simplified your project stack by removing CSS/SCSS, the benefits would pay big dividends. New talent would have less to learn. Devs would have fewer processes to follow and fewer things taking up mental bandwidth.
Still though, switching to a subset of CSS is a big ask. What about pseudo-elements, media queries, animations, and so on? The answer from CSS-in-JS is still developing. Different CSS-in-JS packages have different coverage and approaches for CSS features. But to me, the benefits already outweigh the growing pains. And if you’re into universal apps (native rendering to every platform), then it is pretty cool to have styles that work through React and React Native across web, iOS, Android, and more with the same behavior.
When I read this article I couldn’t help but feel like I was seeing the evolution of a paradigm that’s been superseded. Check out CSS-in-JS and let me know where I’m wrong!
PS — also check out all the benefits of CSS-in-JS that I didn’t mention here, like loading only critical styles with each page load by default (again, don’t manage CSS in isolation when you already manage everything else).