CryptoNetHub

Thinking Horizontally in CSS @layer

I joked the other day:I know what CSS@layeris, and maybe you do too. This won’t be a reiteration of the rhyme and reason of the feature itself. For that, you probably can’t do better thanMiriam’s guide. . . .

· 6 min read

Thinking Horizontally in CSS @layer

I joked the other day:I know what CSS@layeris, and maybe you do too. This won’t be a reiteration of the rhyme and reason of the feature itself. For that, you probably can’t do better thanMiriam’s guide. Rather, this will be about a different way of thinking about layers that might be useful to you. The point of my lame observational joke is that we tend to think of CSS@layeras a vertical concern. This is true of so many other CSS concepts. Like inline styles are above styles applied in a stylesheet. Your user-authored styles are above the user-agent styles that browsers ship and apply to all pages. But the main thing we think about is specificity. IDs are stronger than classes are stronger than elements, and all that. Along comes@layerand it can make us think evenmorevertically. Now, we essentially “stack” layers. Styles in a higher layer are more powerful(regardless of specificity)than styles in a lower layer. So our code can literally be structured like this, which is also a vertical stack. You can also be specific about layering them. @layerreset, defaults, patterns, components, utilities, overrides;Code language:CSS(css)I like that, myself, as it freaks me out a little bit that otherwise each@layeris stacked through source order alone. There is another little bit of syntax that I find interesting: sub-layers. You use a dot/period for this, like:@layercomponents. button { }@layercomponents. card { }@layercomponents. grid { }Code language:CSS(css)So now all these layers are still slotted into the power hierarchy, where thecomponentsis, but then have their own sub-heirarchy, which would be source-order, unless we force it ourselves like:@layerreset, defaults, components, components. button, components. card, components. grid, patterns, . . . ;Code language:CSS(css)The Thing About Components: There Are ManyMaybe you have like 50? 100? 374?And if you’re like me (and a lot of front-end architectures!), you like the one-CSS-file-per-component setup (sometimeslike this). With this many components and many CSS files, the point is: who cares about the vertical ordering of the layers? It just doesn’t matter which component’s styles beat another component’s styles. So you can either just not name them://components/button/button. css@layercomponents {. button{/* button styles */} }Code language:CSS(css)//components/card/card. css@layercomponents {. card{/* card styles */} }Code language:CSS(css)Or do name them (just “in case”), but don’t even bother to order them specifically, because itprobablydoesn’t matter. //components/button/button. css@layercomponents. button {. button{/* button styles */} }Code language:CSS(css)//components/card/card. css@layercomponents. card {. card{/* card styles */} }Code language:CSS(css)A Component Might Have Layered and Unlayered StylesYou can already get some level of CSS scoping to a component, even without any fancy tools. Say you have aCardcomponent (as we’ve been using an example), it could just have that class of Card, and you nest everything beneath that, and that’s that. . card{ . title { }. meta{ }/* etc. */}Code language:CSS(css)Or perhaps you have some naming convention like BEM or whatever. Maybe you get even fancier and use@scopelike:@scope(. card) { }/* or you have a custom element */@scope(design-system-card) { }Code language:CSS(css)You could even usea name-only container query, which I find kind of satisfying for some reason. /* Name a container */. card{container-name: ds-card;container-type: inline-size; }/* Write styles with that name only, no conditions */@containerds-card {. title{/* classes like me won't "leak out" */}. meta{ }/* etc. */}Code language:CSS(css)Literally any of that will work with what I’m about to show. The idea is that you layer the tokens the component uses, and youdon’tlayer scoped stuff. @layercomponents. card {/* intentionally weakened so any other selector can override values */. card{--card-bg: black;--card-color: white;/* etc. */} }/* Scope for protection while retaining some strength */@scope(. card) {background:var(--card-bg);color:var(--card-color); }Code language:CSS(css)The Point is Intentionally Weak TokensI don’t want to have to fight to override tokens. That should be really easy. I don’t want to think about source order or if the selector I’m using is strong enough to do the job. In the code block above, because I’ve used@layerto wrap where I’m setting the tokens, any higher layer,and definitely unlayered CSS,can target the card and override the variables. No fighting. Now We’re in Horizontal-TownImagine you do this on all your components. You’ve basically got two-layer components, but a ton of them. It’s now much less about the vertical ordering. It’s a short, fat, horizontal setup. Maybe calling it “horizontal” is a stretch or a weird way to think about it. But it’s useful to me! I think it’s a bit overwhelming to consider the vertical stacking order of potentially hundreds of items. But you don’t have to care. You don’t have to think of it that way. You can think of it horizontally, where each component is essentially just a two-stacker. Here’s a Real-World Example of it Being Very UsefulThis is literally what we do with components at CodePen. Here’s a screenshot with a couple of random component CSS files open side by side. Notice we use. module. scssfiles. Both Sass and CSS modules. That’s neither here nor there for the point of this post, but it works great for us. We also have “library” components, which are “dumb,” meaning they can’t do things like query for data and don’t have other custom components as children. Those are vertically layered underneath just “components”, which are more compositional. Generally, the root node of a component has the class “root” as a convention for us, and to avoid having to name it explicitly. It literally doesn’t matter what it’s named because of the scoping. The important bit to this article is how we are@layering all the custom properties that each component uses. This makes it very easy for us to override them whenever needed, which is good, actually, and why they are there in the first place. I really dislike it when we find ourselves using like. root. rootstyle class names just to increase specificity to make a custom property win somewhere. Putting custom properties into a layer at all makes them so “weak” that pretty much anything can override them, even when targeting the element with a very weak (yet unlayered) style. DemoNow I could take a weak-but-meaningfully-specific selector to the cards and override a custom property (token):ui-card{--card-font: fantasy; }Code language:CSS(css)And bask in typographic glory:

Source: frontendmasters.com

More Layer 2 news