How Will Web Components Change CSS Architecture?
Currently there are two main approaches to writing CSS: component-based and utility-based. (I’ve also seen this referred to object and functional CSS, respectively.)
A component-based approach establishes a 1-to–1 relationship between the HTML and the selectors. It does so by codifying a design pattern and applying it to an HTML element.
For example, a button is codified in the CSS with a
.button class and applied to an element.
Since there’s a selector for every design pattern, as the number of design patterns on your site grows, so does the size of your CSS.
Theoretically, there’s a cap to the number of design patterns you will or should have on your site. However, evolving designs and growing sites often continue to add to the codebase without removing old designs. I’ve seen sites with ten years worth of CSS that continued to be cobbled together.
SMACSS falls squarely in this camp.
At the other end of the spectrum is a utility-based approach. It establishes a 1-to–1 relationship between the selectors and the CSS properties.
For example, a button—consisting of a collection of CSS properties—receives a number of classes to style the HTML.
There are only so many CSS properties and only so many values we set with those properties. As a result, the size of the CSS reaches a cap.
Atomic CSS falls squarely in this camp.
In the Middle
Of course, there are people (er, most people, probably) that choose to blend these two styles together. Some use a component-based approach for some things, like buttons and inputs, and then use a utility-based approach for other things, like layout and spacing.
How that CSS actually gets delivered to a user is a factor in all this.
Most projects I’ve seen go all in. All of the CSS for a project is bundled together into a single file, minimized, then cached.
When I was working at Yahoo!, the reason for the component-based approach was to be able to bundle only the CSS needed for the page and no more. Only when new pages with new components were loaded was the CSS for those components loaded. This was done through a “combo handler” that could combine the individual CSS files together at request time and cached.
If you’ve looked into Web Components, that last bit should feel really familiar.
If a Web Component is only requested when it’s used then the embedded CSS will only be used when the Web Component is requested.
Web Components actually consist of 4 different technologies: custom elements, HTML imports, Templates, and Shadow DOM. When I refer to Web Components in this article, I refer to them as a collective whole. An actual project not using HTML imports, for example, might use a different strategy for bundling and loading components into a project. A project could still bundle the whole app into one fell swoop like many people do today. In which case, none of what I’m talking about here really applies.
That means that a component-based approach allows us to localize CSS to a given component and get the performance benefits of only using that CSS when it’s needed.
Many in the React world using inline CSS are essentially doing the same thing. They’re writing CSS in the component and that CSS only gets used when the component is requested.
As you can probably guess, my approach (with SMACSS, natch) to building a site using Web Components doesn’t really change. Some concerns like using naming convention to namespace CSS fall by the wayside due to the Shadow DOM.
For those using a utility-based approach will still need to go through a bundling process but with the file size being (theoretically) reasonably sized, this isn’t really an issue. Each Web Component just has to say that it wishes to use the same CSS file.
It remains to be seen whether a micro-delivery approach will create a better user experience than a macro-delivery approach.
Does 240KB of CSS (using a component-based approach) broken down into a dozen requests of 20KB each over multiple pages create a better experience than a single 50KB request (using a utility-based approach)?
I’ve seen pros and cons to both scenarios. The micro-delivery approach currently depends on http2 taking off (which it is and will) and while the performance improvements look good overall, we’re seeing that it’s not necessarily an instant panacea†‡. We’ll continue to see server-side optimizations that can be introduced to improve delivery performance.
† Khan Academy saw a performance drop from serving up too many files over http/2.
‡ Dropbox saw a decrease in performance with POST requests, which has subsequently been fixed.
(I don’t mean to be alarmist over http2. I honestly believe that these are small hiccups and that we should be moving to http2 as soon as we can.)
All that to say that I don’t think the CSS architecture landscape will really change much. We’ll likely see more discussion around what should be defined with a Web Component and what should be defined outside of that. Although, it’s possible that some completely different approach could be introduced.