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.
Step-by-step resolution
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: paintadditionally 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.
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-indexchanges visual layering; check that nothing that should cover the element now sits behind it. isolation: isolateprevents blend modes from reaching outside the element, which may be intentional elsewhere.contain: paintclips 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.
Related
- Layer Promotion & will-change Strategy — the parent topic
- Auditing Compositor Layers in the DevTools Layers Panel — the workflow for reading reasons
- 3D Transforms, perspective and backface-visibility Costs — another source of extra layers