We’ve been using Cascade Layers in our CSS here at CodePen quite a bit lately. A pretty clear non-complicated use case is that we tend to apply one class to stuff. That includes a “library” component. So imagine the prototypical Button, which would have a .button
selector applying styling to it. But this library component also accepts a class so that usage of the component can apply it’s own styles if needed.
Like this:
<button class="library-button usage-class-button">
Since we’re building React components, it expresses like:
<Button className={classNames(styles.root, props.className)}>
So now we’ve got two selectors that are exactly the same in specificity. Which one wins? It will come down to source order. And to be quite honest, I’m not really sure how that will play out in every single scenario the component could be used on CodePen. The styles might be bundled, sometimes by different build processes, and placed into combined CSS files or even sometimes loaded dynamically. That has led to the occasional situation where we artificially increased the specificity of the class selector we’re passing in just so it for sure wins. .myClass.myClass
, for instance. That’s dumb.
Instead, we can force our library components to have intentionally less powerful styles via Cascade Layers. So our “root” style can be like:
@layer library {
.root {
}
}
Alone, that works. That means the .myClass
stuff will always win, as unlayered styles are always stronger than layered styles. But to keep us honest we have setup the order such that we can slot things in in an expected way going forward:
@layer library, root, page, component;
That’s in our global CSS such that we have those levels to poke in at, when we want to use layering but at a specific level of strength.
Layers is a nice enough API that we can also do sub-layering. Meaning rather than just @layer library { } wrapping all library components, we’d actually do something like this for Button:
@layer library.Button {
}
That means it’s still slotted in at the library level, but if we needed to at some point, we could declare the order of components such that one could beat another. We haven’t needed it yet, but it feels like the right move.
I can absolutely see how people can think Cascade Layers are useless. (Manuel Matuzović doesn’t actually think this, but he makes some points with code on how people could.) They didn’t strike me as terrible useful right away as something you could “sprinkle in” to a project until I came across out own use case here. Mostly I thought of it as “put Bootstrap in a layer, unlayer your own styles” as the #1 use case. But now I’m starting to think “keep specifically generally flat, and when they are conflicts use layers instead of increased specificity” as a pretty big use case as well.
Let’s do some bonus CSS links because we can:
- Andy Bell: How we’re approaching theming with modern CSS. It’s heavy on custom properties and allowing a “theme” over override a limited set of them (in a “flair” layer) to have maximum impact.
- Andrew Walpole: Opinions for Writing Good CSS. Essentially a list of best practices. I really like the last one: “Don’t. Paste. CSS.”
- Hristiyan Dodov: CSS Can Get You in Jail. Spoiler, it’s about how browser render counters on nested lists. In a legal document, you just can’t risk potential browser rendering differences, and should hard-code the numbers.
- Josh Comeau: The Secret Mechanisms of CSS. Just one great quote: “the layout algorithms are kind of like functions and the CSS that we write are the arguments that we pass to those functions”.
- (Anonymous?) Use CSS’ `only-child` instead of `if/else` logic. Sometimes I feel like if/else logic for views makes more sense at the HTML level than the CSS level. But because they are using Tailwind here, it kinda does put that logic at the HTML level.
- Adam Argyle: Headless, boneless, skinless & lifeless UI. Like Adam says, these aren’t really terms that anyone is actively using to describe UI frameworks, but maybe we should be, because these are pretty major distinctions that you’d chose for very different reasons.