Layers
CSS recently introduced a new feature called layers
to the cascade specification (opens in a new tab).
This important feature allows more granularity and control over CSS specificity and overrides, especially when discussing shared libraries and design systems.
Layers allow authors and Lualtek UI consumers to define specificity layers independently from when a specific style appears on the page and the parsing order.
Here is a quick example, in the code below, if we remove the @layer reset
declaration the p
element would be blue
.
Still, since the browser parses the CSS placed outside any layer (implicit final layer) after all the styles coming from layers, the p
element is red
even if p[data-blue]
has higher specificity.
p {
/* specificity of 0,0,1 - implicit (final) layer */
color: red;
}
@layer reset {
p[data-blue] {
/* specificity of 0,1,1 - explicit "reset" layer */
color: blue;
}
}
Lualtek UI layers
Lualtek UI defines 6 CSS layers, referenced internally inside the packages that compose the design system:
@layer core, utilities, plain-components, components, overrides, themes;
Let's see a real example. The Button
component declares its styles inside the components
layer:
@layer components {
.Button {⋯}
}
Now, the Snackbar
needs to override some properties of the button, but we cannot use (opens in a new tab) !important
. Layers come to rescue by allowing us to section the cascade in different layers of specificity. Now let's see what the Snackbar
can do to solve this issue.
@layer overrides {
.Action {
color: var(--snackbar-bg);
background-color: var(--snackbar-fg);
&:is(:active, :hover) {
color: var(--snackbar-bg);
background-color: var(--snackbar-fg);
opacity: 80%;
}
}
}
Since we declared the overrides
scope after the components
layer, it has higher precedence in the cascade and specificity computing. This way, the styles declared inside a layer overrides
will win over the ones declared inside the layer components
. This is the same as using !important
but without the drawbacks. Additionally, any style not declared inside a layer will be placed inside the implicit (final) layer, which has the highest precedence in the cascade and so will win over any other layer.
Advantages for consumers
When consumers need to write down their styles and overwrite some properties, any CSS defined in their project will always win over the styles coming from the design system unless the user declares them inside a new and local CSS layer.