Stop the cascade?

As you know, the CSS Cascade is a strange creature; all-seeing, ever-present, determining how everything will look, whether you like it or not. Great if it is a document, something you can keep in your head and reason about. However, a significant part of the Web isn't document based but is transpiled around applications with custom UI's that see the singular CSS Cascade as a hostile overlord. I won't go into the nitty-gritty of the Cascade, as Bramus has done that better than most. I do think we should take a closer look at the at-rule @layer and other new CSS features, as these will make it much more manageable.

The rise of JavaScript came with significant changes and advancements that made the Web a more viable platform for our ambitions. Somehow CSS got left behind in the (gold)rush[1]. Was it because we viewed JavaScript as how the Web behaved and CSS as how it looked? The decoration wasn't what was driving the (r)evolution. However, the notion that CSS was for only decoration was a bit of a false narrative. The Web, its trifecta HTML, CSS and JavaScript are not separate silos but form a symbiotic relationship. Changes to one changes or breaks the other.

/* Rule: Scoped by BEM convention  */
.block__element--modifier {
/* your statement block */
<!-- Tag Element: Scoped by BEM convention -->
<div class="block__element--modifier">
<!-- your content -->
/* DOM Element: Scoped by BEM convention */
const elm = document.querySelector('.block__element--modifier');
/* Do something with `elm` */

To make this work across sites and instances, you need to have control over the classname and have access to the HTML. That's not always possible, which is why you see solutions siloed into an app or site.

For truly autonomous components, we need to organise our code in a way that allows for scoping and isolation robustly and predictably that convention alone doesn't provide. What if you could use a set of design values and 'assemble' features that do not need to be too concerned about the Cascade without trying to hack around it?

Step one: Layers

/* Layer definitions: 
* Base: For resets and normalize rules
* System: Your design system. These can be distilled from a library
* Theme: Bespoke design system values for your implementation
* Colors, Spacing but also Animation and Layout
* Components: Where your (white label) component library lives.

@layer base, system, theme, components;

The theme layer comes before the components because a theme should inform a component and never break it. We can also nest layers, but like everything, this can bring unnecessary complexity, so use them judiciously.

/* Optional component (sub)layer definitions:
* Props: Custom properties in/for the component
* Selectors: Rules of the component using only props
* Modifiers: Selector and prop additions for state and context

@layer components {
@layer props, selectors, modifiers;

The advancements in CSS are moving in a direction that allows a better synergy with the current JavaScript landscape that has been doing its own thing with styling. Lots of strong opinions but little consensus. However, it's reasonable to say that the way the Web works has largely been rejected by those that create web applications. Let's be clear, the original document-based styling is an insane way to make large websites[2]. One thing that seemed to work is to keep the Cascade as short as possible so we can pretend it doesn't exist. Solutions evolved like OO CSS, BEM and SMACS. These still rely on a shared understanding of the cascaded modules and components. Doable but not sustainable in the long run. We will soon have several CSS features that will, in my opinion, go a long way to solve this brittle way of styling. These solutions touch on the main areas were need more control.

  1. Styling assets across multiple domains
  2. Viewport and the 'page' arrangements
  3. Component and content styling

Step two: Passing props

@import '' layer(theme);

@layer components {
/* Add Design System (ds) values where appropriate */
@layer props {
article {
--card-display: grid;
--card-columns: repeat(auto-fill, min(25ch, 100qcw));
--card-border-radius: var(--ds-radius-fullscreen);
--card-picture-ratio: var(--ds-ratio-square);
@layer modifiers {
@container card (width >= 25ch) {
article:has(picture) {
--card-picture-ratio: var(--ds-ratio-landscape);
@container card (width >= 15ch) {
article {
--card-border-radius: var(--radius-2);
@layer selectors {
:host(comp-card) {
display: block;
container: card / inline-size;
article {
display: var(--card-display);
grid-template-columns: var(--card-columns);
border-radius: var(--card-border-radius);
picture {
aspect-ratio: var(--card-picture-ratio);

The push was to get ALL these three features into a stable browser version. Mission accomplished. To make this more robust in light DOM, we may need the addition of @scope. However, that still seems a few years out.

  1. Cascade layers: Context across your page(s)
  2. Container queries: Web Component in the context of a document
  3. Parent Selector: Context of the Web Component based on its content

And Cascade Layers, more than the other two, is a fundamental shift in how CSS works. Everything will change; libraries can be constructed so that they behave more predictably. The other features will change how these libraries are structured.

Let it flow

Component-safe styling has arrived, for real, this time. With Web Components on the rise, it seems that the web platform has become a more viable option than we could have thought just a few years ago.

  1. I wondered if Houdini would change that, and indirectly it has. Even though it is still not clear if we'll ever get there with Apple being a notable holdout. The whys and what for's is a whole other post and would be purely speculative. ↩︎

  2. A Site with a lot of URL's with many markup variations ↩︎

Posted on: September 4th, 2022

Next Post

Automated color modes

Color Contrast is currently an experimental feature available under a flag in Safari and Chrome.

Previous Post

25 years

I started working on the Internet 25 years ago.