New Apr 15, 2026

Warning: containment breach in cascade layer!

More Front-end Bloggers All from dbushell.com (blog) View Warning: containment breach in cascade layer! on dbushell.com

CSS cascade layers are the ultimate tool to win the specificity wars. Used alongside the :where selector, specificity problems are a thing of the past.

Or so I thought. Turns out cascade layers are leakier than a xenonite sieve. Cross-layer shenanigans can make bad CSS even badder. I discovered a whole new level of specificity hell. Scroll down if you dare! There are advantages too, I’ll start with a neat trick.

Neat trick

To setup this trick I’ll quickly cover my favoured CSS methodology for a small website. I find defining three cascade layers is plenty.

@layer base, components, utility;
It’s important to name layers such that maximum bikeshedding is achieved.

In base I add my reset styles, custom properties, anything that touches a global element, etc. In components I add the core of the website. In utility I add classes that look suspiciously like Tailwind, for pragmatic use. Visually-hidden is a utility class in my system.

Figma betrayel

I recently built a design where many headings and UI elements used an alternate font with a unique style. It made practical sense to use a utility class like the one below.

@layer utility {
  .font-brand {
    font-family: "Garish Sans";
    letter-spacing: 20%;
    text-transform: uppercase;
  }
}
I don’t wrap every class in @layer I’m just doing that here for clarity.

This is but a tribute, the real class had more properties. The class is DRY and easily integrated into templates and content editors.

Adding this to the highest cascade layer makes sense. I don’t have to worry about juggling source order or overriding properties on the class itself. I especially do not have to care about specificity or slap !important everywhere like a fool.

This worked well. Then I zoom further into the Figma picture and was betrayed!

Escape hatch

The design had an edge case where letter-spacing varied for one specific component. It made sense for the design. It did not make sense for my system.

If you remember, my utility cascade layer takes priority over my components layer so I can’t simply apply a unique style to the component.

@layer base, components, utility;

For the sake of a demo let’s assume my component has this markup.

<div class="Component">
  <h2 class="font-brand">Heading whatever</h2>
</div>

I want to change back to the normal letter-spacing.

@layer components {
  .Component h2 {
    letter-spacing: normal;
  }
}

Oops, I’ve lost the specificity war regardless of what selector I use. The font-brand utility class wins because I set it up to win.

My ā€œescape hatchā€ uses custom property fallback values.

@layer utility {
  .font-brand {
    font-family: "Garish Sans";
    letter-spacing: var(--letter-spacing, 20%);
    text-transform: uppercase;
  }
}

In most cases --letter-spacing is not defined and the default 20% is applied.

For my edge case component I can ā€˜configure’ the utility class.

@layer components {
  .Component {
    --letter-spacing: normal;
  }
}

I’ve found this to be an effective solution that feels logical and intuitive. I’m working with the cascade. It’s a good thing that custom properties are not locked within cascade layers! I don’t think anyone would expect that to happen.

Important breach

In drafting this post I was going to use an example to show the power of cascade layers.

@layer components {
  .Component h2 {
    letter-spacing: normal !important;
  }
}
This code didn’t fail as exepected.

I was going to say that not even !important wins. Then I tested my example and found that !important does actually override higher cascade layers. It breaches containment too!

What colour are the paragraphs?

@layer one, two, three;

@layer one {
  p { color: blue !important; }
}

@layer two {
  p { color: white !important; }
}

@layer three {
  p { color: black; }
}

Suffice it to say that things get very weird. See my CodePen.

Spoiler: blue wins.

I’m sure there is a perfectly cromulent reason for this behaviour but on face value I don’t like it! Bleh! I feel like !important should be locked within a cascade layer. I don’t even want to talk about the inversion…

I’m sure there are GitHub issues, IRC logs, and cave wall paintings that discuss how cascade layers should handle !important — they got it wrong! The fools! We could have had something good here! Okay, maybe I’m being dramatic. I’m missing the big picture, is there a real reason it has to work this way? It just feels… wrong? I’ve never seen a use case for !important that wasn’t tear-inducing technical debt.

Permeating layers with !important feels wrong even though custom properties behaving similar feels right. It’s hard to explain. I reckon if you’ve built enough websites you’ll get that sense too? Or am I just talking nonsense?


I subscribe to the dogma that says !important should never be used but it’s not always my choice. I build a lot of bespoke themes. The WordPress + plugin ecosystem is the ultimate specificity war. WordPress core laughs in the face of ā€œCSS methodologyā€ and loves to put styles where they don’t belong. Plugin authors are forced to write even gnarlier selectors. When I finally get to play, styles are an unmitigated disaster.

Cascade layers can curtail unruly WordPress plugins but if they use !important it’s game over; I’m back to writing even worse code.

Update for 16 Apr 2026

Thinking about this more, it makes sense from the perspective of giving users the final word, and browsers having a spec-defined way to achieve that.

Practically though, for developers, even when everyone is on the same team a single !important can ruin the party. In scenarios like WordPress these problems manifest as more than just gnarly selectors. Bad and unmaintainable CSS snowballs into bugs and broken dreams.

In short, it might make sense in theory but in practice the evidence suggests otherwise. I remain unconvinced it needed to be this way.


Thanks for reading! Follow me on Mastodon and Bluesky. Subscribe to my Blog and Notes or Combined feeds.

Scroll to top