Stacking Contexts and Accidental Layer Explosions

Part of Layer Promotion & will-change Strategy in Performance Budgeting & GPU Architecture.

The problem

A sticky promotional banner animates a subtle drift. It is one element with one will-change: transform. The Layers panel shows fourteen layers on the page, memory is several times what was expected, and scrolling is worse than before the banner existed.

The banner is not the only promoted element. Everything that overlaps it, and everything above those elements in paint order, was promoted too.

Root cause analysis: correctness forces promotion

The compositor draws layers in order. If an element is promoted to its own layer, anything that must paint on top of it has to be promoted as well — otherwise it would be drawn into a lower layer and appear underneath. This is overlap-driven implicit promotion, and it cascades: a promoted banner promotes the card that overlaps it, which promotes the badge on the card, and so on.

Paint order decides who is above. Elements paint in the order defined by stacking contexts, z-index and document order. An element that comes later in paint order and overlaps a composited element is at risk of implicit promotion. An element that paints below is not.

Stacking contexts are created by more than z-index. opacity below 1, transform other than none, filter, backdrop-filter, isolation: isolate, will-change naming any of those, mix-blend-mode, contain: paint, position: fixed and top-layer elements all create one. Each context changes which elements can be interleaved with which, and therefore which overlaps matter.

The fix is usually structural. Either stop the promoted element overlapping unrelated content, or make it the topmost thing in its area so nothing has to be lifted above it. Increasing the animated element’s z-index is often enough: if it is on top, the elements beneath it do not need promoting.

Stacking contexts also limit the damage. Because promotion effects are contained within a stacking context in many cases, putting an animated element inside its own isolated context can stop the cascade from reaching the rest of the page.

One promotion, four layersEach element that must paint above a composited element is promoted too.One promotion, four layersTooltip above the badgeImplicit: overlaps a composited elementBadge above the cardImplicit: overlap cascade continuesCard overlapping the bannerImplicit: paints above the bannerSticky banner (will-change)Explicit: the promotion you asked forPage contentBelow: unaffected
Each element that must paint above a composited element is promoted too.

Step-by-step resolution

Containing an explosionRead the reasons, then change structure rather than adding more hints.Containing an explosion1Open the Layers panel and list layers with their compositing reasons.Explicit versus implicit is visible2Find the one explicitly promoted element at the root of the cascade.The starting point3Check which elements overlap it in paint order.The implicit set4Raise the animated element's z-index so it paints above them.Nothing needs lifting above it5If it must sit below, move it out of the overlap or isolate the region.Cascade stopped6Delete stacking-context properties added only for layering.
Read the reasons, then change structure rather than adding more hints.

Production code pattern

/* Before: the banner is promoted and sits under cards that overlap it. */
.banner-old {
  position: sticky;
  inset-block-start: 0;
  will-change: transform;
  /* cards overlap it as they scroll past: each gets implicitly promoted */
}

/* After: the animated element paints above everything it overlaps. */
.banner {
  position: sticky;
  inset-block-start: 0;
  z-index: 20;                          /* on top: nothing must be lifted above it */
  isolation: isolate;                   /* its own stacking context, cascade contained */
}
.banner[data-animating] {
  will-change: transform;               /* only while it actually drifts */
}

/* Cards no longer need their own layers. */
.card {
  /* removed: opacity: 0.999 and translateZ(0) hacks that created contexts and layers */
  position: relative;
  z-index: 0;
}

/* A region that contains its own animation, isolated from the rest of the page. */
.widget {
  isolation: isolate;
  contain: paint;                       /* also a repaint boundary */
}

@media (prefers-reduced-motion: reduce) {
  .banner[data-animating] { will-change: auto; }
}

Rendering Impact: fewer composited layers, and therefore less texture memory and less per-frame compositing work. contain: paint additionally bounds repaints, as covered in using contain to scope repaints.

isolation: isolate is the least invasive way to create a stacking context: unlike opacity: 0.999 or transform: translateZ(0), it does not itself promote a layer or change rendering. It is the right tool when the goal is purely to control stacking.

Composited layers on the same pageOne animated sticky banner; the difference is paint order and isolation.Composited layers on the same pageBanner promoted, under overlapping cards14 layersBanner raised above with z-index5 layersBanner raised and hinted only while animating2 layers
One animated sticky banner; the difference is paint order and isolation.

Reading compositing reasons

The Layers panel gives each layer a list of reasons, and the vocabulary is worth learning. Reasons naming an author declaration — “has a will-change: transform”, “is a fixed position element”, “has a 3D transform” — are explicit: something in the CSS asked for the layer. Reasons naming relationships — “overlaps other composited content”, “is a child of a composited element”, “squashed into another layer” — are implicit: the browser had no choice given the paint order.

The strategy follows from the split. Explicit layers are removed by changing the declaration that asked for them. Implicit layers are removed by changing structure — paint order, overlap, isolation — because the declaration causing them is elsewhere. Most explosions are one explicit layer plus a long implicit tail, so finding the single explicit root is the fastest path, as described in auditing compositor layers in the DevTools Layers panel.

Verification checklist

Constraints and trade-offs

  • Raising z-index changes visual layering; check that nothing that should cover the element now sits behind it.
  • isolation: isolate prevents blend modes from reaching outside the element, which may be intentional elsewhere.
  • contain: paint clips descendants, so escaping content needs another home.
  • Some implicit promotion is unavoidable when overlays genuinely overlap animated content.
  • Compositing reason strings vary between browser versions.

Frequently asked questions

Why does one animated element create many layers?

Anything that must paint above a composited element has to be composited too. That requirement cascades through overlapping elements in paint order.

Does z-index affect compositing?

Indirectly. It changes paint order, which decides which elements must be promoted to stay above a composited element. Raising the animated element often removes the need to promote others.

Which properties create stacking contexts?

Among others: opacity below 1, transform, filter, backdrop-filter, isolation: isolate, mix-blend-mode, contain: paint, position: fixed, and will-change naming any of those.

How do I tell explicit from implicit promotion?

Read the compositing reasons in the Layers panel: declaration-based reasons are explicit; overlap and child relationships are implicit.