@layer

Cascade Layers

This one took me the longest to get, mostly because it doesn’t slot into either tie-breaker I already knew. The cascade normally compares specificity, then falls back to source order. Layers add a step in front of both: group rules with @layer, declare the order once, and that order settles every conflict between layers before specificity gets a vote.

With @layer

reset
component
override
urgent

Success green. override wins, even though it’s written earlier in the file and has a plainer selector than component, because it’s named later in the layer order.

Without @layer

reset
component
override
urgent

Still blue, checkbox or not. .planet.planet.planet simply out-specifies override and urgent. Without layers, whoever wrote the more specific selector wins, regardless of intent.

The CSS

Copied
/* Precedence is decided by this order, not by where each
   block below sits in the file and not by specificity. */
@layer reset, component, override, urgent;

@layer reset {
  .planet { color: var(--text-secondary); }
}

/* Written second, right after reset, yet still wins outright over the
   other three, including the two below it: named LAST above. Checking
   "Urgent" adds this class to the planet. */
@layer urgent {
  .planet.urgent { color: var(--danger-text); }
}

/* Plain selector, written before component below, still wins over it:
   named later above. */
@layer override {
  .planet { color: var(--success-text); }
}

/* Higher specificity, written last, still loses: named earlier above. */
@layer component {
  .planet.planet.planet { color: var(--accent-text); }
}

How it works

The With @layer panel styles its planet with three rules in three layers, ordered in one statement: @layer reset, component, override, urgent;. The override rule has a plain selector and sits earlier in the file than component, whose selector is deliberately tripled for specificity. It still wins, because layer order is compared first.

The Without @layer panel has the same three rules in the same order, minus @layer. Now component’s increased specificity beats everything, so ticking “urgent” does nothing. That’s the win: you say “utilities always beat components” once, instead of policing every selector by hand.

Widely supported

Chrome / Edge99+Safari15.4+Firefox97+
:has() · tic-tac-toe

turns, wins and reset, all CSS

@property · counter()
hover me
cos() · sin()

trigonometry, in CSS

:has() quantity query

switch guests on