All is not

Back when CSS cascade Layers became available in major browsers – you know that thing where you @layer your css – i read all the articles and watched all the videos and came to the conclusion, it's not for us to use yet.

"For us" means the company i work for. Our clients are mainly public organizations. For example, our websites supported Internet Explorer all the way up until Microsoft finally pulled the plug.

Fast forward two, two and a half years, i thought "oh well, now it's about time". Given that un-layered css has the highest priority, i figured we might as well start with adding our package styles to a @layer vendor.

Unfortunately, postcss begged to differ. Based on our browserslist the plugin pack "postcss-preset-env" concluded, it is time to enable the plugin "postcss-cascade-layers". Now postcss-cascade-layers tries to make something backwards compatible which is not supposed to be backwards compatible. I guess, it might need an all-or-nothing approach. A rabbit hole which i don't want to slide down.

Side note: Opera mini… What's the deal here? Why is this not considered a dead browser?

But what actually happened?

In one of the css (sass) files, i added:

@layer vendor {
    @import "~@our-package/forms";
}

.form-element__input {
    border-radius: 4px;
}

Nothing else. The rest of the css is un-layered. Only after a while i realized that submit button's style was pretty off, off as in it was not visible due to non-applying styles. And that's when i discovered, not everywhere, but quite a few occurrances of :not(#\#) in the css output.

Input of a typical base style:

*,
::after,
::before {
    box-sizing: border-box;
}

Output

*:not(#\#),
:not(#\#)::after,
:not(#\#)::before {
  box-sizing: border-box;
}

I quickly suspected postcss. At first, i thought it was something from the plugin "postcss-selector-not". This made no sense since this plugin only cares about selector lists inside a :not() pseudo classs, but you know how it is. I've tried to find a setting which produces the right outcome without sacrificing our browser requirements. A form on a public organization website is too important for all users.

Only after stepping away from the desk and rubber-ducking with my partner a tiny @layer was hovering before my inner eye. Back to the desk, on to the the plugin's readme' and, alas, there we were. Many :not(#\#)s creating many specificities.

Btw, The plugin postcss-cascade-layers does to not apply :not(#\#)s to everything. As i was already way over budget in this task, i didn't investigate further.

Back to the submit button. We use a button reset alongside a base style for links and buttons, which were now taking precendence over the actual class-based styles. In the mix was also a custom property from the class-based rule block which was applied to the element in question, but missing the rest of the class-based rule block. Uff…

Solution

I've removed the layer and went back to the previous way of handling our package overrides and enhancements.

Conclusion

This was a good reminder: CSS Cascade Layers is not natively an all-or-nothing approach. However, i suppose it is when using any kind of postcss, polyfill, make-it-backwards-compatible.

And somewhere along the way is an invitation waiting for me to explore the postcss world more. Especially, postcss-preset-env does many things for you and it does so in good ways. But every once in a while, things derail and it's time to delve into the build tools.

I want to close with a couple quotes by Miriam Suzanne from "A Complete Guide to CSS Cascade Layers":

[…] layers are intended as foundational building blocks of an entire CSS architecture […]

Since layered styles have a lower priority than default “un-layered” styles, this is a good way to start using cascade layers without re-writing your entire CSS codebase.