Cascade Layers

@layer groups rules into named layers whose order you control, independently of how specific the selectors inside them are. It is the CSS at-rule described at MDN Docs, and its main use is keeping a framework’s rules from outranking your own.

@layer framework, app;

@layer framework {
  #title {
    color: white;
  }
}

@layer app {
  text {
    color: red;
  }
}

#title is far more specific than text, and loses anyway: app is the later layer, and the layer order is consulted before specificity.

Declaring layers

FormWhat it does
@layer name { … }Puts the rules in name, creating it if it is new
@layer name;Creates name in this position without adding rules
@layer a, b, c;Creates several at once, in that order
@layer { … }An anonymous layer, which nothing else can add to
@layer outer { @layer inner { … } }A sub-layer of outer
@layer outer.inner { … }The same sub-layer, addressed by its full name

A layer takes its position from where its name is first seen in the document, so reopening it later adds rules to the original position rather than moving it to the end. That is what makes the leading @layer a, b, c; statement worth writing: it fixes the whole order up front, whatever order the blocks then arrive in.

How layers order

  1. Unlayered rules beat every layer, wherever they appear.
  2. Among layers, the later layer wins.
  3. Inside one layer, specificity and source order decide as usual.
  4. A sub-layer sits inside its parent’s slot, and loses to the parent’s own rules. So outer.inner loses to outer, and both lose to any layer declared after outer.
  5. !important reverses 1 and 2: an important declaration in the earliest layer wins, and an important declaration in any layer beats an important unlayered one.

Layers nest inside @media and @supports in either order. A layer declared inside a @media block still takes its position from the source, so a condition turning on or off never reshuffles the order of rules that have nothing to do with it.

@layer base {
  view {
    background-color: black;
  }
}

@media (min-width: 600px) {
  @layer base {
    view {
      padding: 20px;
    }
  }
}

Layers and stylesheets

The layer order belongs to the whole context rather than to one stylesheet. A name means the same layer in every sheet, and a layer takes its place from the first sheet that mentions it — so a later sheet reopening a layer adds to it where it already is, instead of making a new one at the end. That is what makes a leading @layer statement worth putting in a sheet of its own.

/* inserted first: fixes the order for everything that follows */
@layer framework, app;
/* inserted later: lands in the slot above, not after `app` */
@layer framework {
  #title {
    color: white;
  }
}

Removing a stylesheet gives up the layers that only it declared, so the order is always what the currently attached sheets say it is. Inserting or removing one can therefore move a layer — a sheet declaring framework.reset slots that in ahead of framework — and the rules already in the layers that moved are reranked to match.

revert-layer

revert-layer rolls a declaration back to the value it would have had if its own layer had never been declared. It is the CSS keyword described at MDN Docs.

@layer base {
  text {
    color: black;
  }
}

@layer theme {
  text {
    color: white;
  }

  #title {
    color: revert-layer;
  }
}

#title is black. The whole of theme goes, not just the declaration that reverted it, so the color: white in the same layer is passed over as well and base is what remains. It keeps rolling back as far as it has to: if the value it lands on is a revert-layer too, that layer goes next.

revert-layer in an unlayered declaration has no earlier layer to reach, so it rolls back further than the layers — past every rule of the same origin, which is what revert does. The same is true of one in the earliest layer with nothing behind it.

An !important declaration reverts its layer at its own priority, and the layer order for those is reversed, so revert-layer !important in the earliest layer lands on the important declaration in the next layer along.

Limitations

A context has room for 30 distinct layer positions. Beyond that, every further layer shares the last position, so they fall back to specificity and source order against each other. Sub-layers count towards the same 30.

@import is not implemented, so @import url(…) layer(name) has nothing to attach to.