Skip to content
Platform internals & deliveryLesson 3 of 4Advanced15 min

Containment & isolation

CSS containment lets you tell the browser a subtree is independent, so it can skip work. Learn contain, content-visibility, and will-change — and their costs.

Kept on this device only.

In one line: containment is a promise you make to the browser — "this subtree is independent" — and in exchange the browser gets to scope or skip layout, paint, and style work it would otherwise do for the whole page.

What it is

CSS containment is a small family of declarations that let you tell the browser an element's subtree is self-contained. Once the browser trusts that promise, it can isolate the work for that subtree — and, in the strongest case, skip it entirely until it's needed.

There are three tools, in rising order of power and cost:

  • contain — isolate an element's layout, paint, size, or style so changes inside it can't ripple out to the rest of the page.
  • content-visibility — go further and skip rendering an off-screen subtree altogether until it scrolls near the viewport.
  • will-change — a different kind of hint: promote an element to its own compositor layer ahead of an animation so the first frame is smooth.

The first two are about not doing work; the last is about doing work early. All three are promises, and the browser keeps its side only if you keep yours.

Why it matters

By default the browser is pessimistic: it styles, lays out, and paints everything in the document, including the hundreds of feed items, comments, or list rows scrolled far below the fold. On a long page that's a lot of work for pixels nobody can see yet, and it shows up as a slow first render and janky scrolling.

Containment flips that. When you can honestly promise that a card, a panel, or a list item doesn't affect the layout of anything outside it, the browser can scope reflow to that box — or, with content-visibility, defer the whole subtree until it matters. On a long feed, that's the difference between laying out everything up front and laying out only what's near the viewport.

See it

Live demo
Tweak it3
3
Compare containment techniques — what each isolates, the win, and the cost to watch.

Switch between the techniques and read what each one isolates and what it costs. Baseline does no special work; contain scopes layout and paint to the box; content-visibility skips off-screen subtrees entirely; will-change trades memory for a smoother animation start.

How it works

contain takes a set of values you can combine: layout (the subtree's layout can't affect the outside, and vice versa), paint (descendants are clipped to the box and never paint outside it), size (the element's size doesn't depend on its children — you must give it a size), and style (scopes certain style effects like counters). There are shorthands: contain: content is layout paint (plus style), and contain: strict is layout paint size (plus style). The common, safe pairing for an independent widget is contain: layout paint.

content-visibility: auto is the big lever. It tells the browser to skip rendering a subtree's contents while it's off-screen, then render it lazily as it nears the viewport. The catch is layout: a skipped subtree has no intrinsic size, so without help it collapses to nothing and the scrollbar lurches as items render. You fix that with contain-intrinsic-size, which reserves an estimated size for the skipped box so the page geometry stays stable.

will-change is the odd one out. It doesn't skip work — it asks the browser to promote an element to its own compositor layer before an animation starts, so the layer is ready for the first frame. Each layer costs GPU memory, so it's a hint to use sparingly and remove once the animation is done, not a property you leave on permanently.

Build it

The canonical use of content-visibility is a long list whose rows mostly start off-screen. Give each row content-visibility: auto to skip off-screen work, and contain-intrinsic-size to reserve space so the scrollbar and layout stay stable:

CSS
A long-list row that skips off-screen render without layout jank
.feed-item {
  /* Skip layout & paint for this row while it's far from the viewport,
     then render it lazily as the user scrolls it into view. */
  content-visibility: auto;
 
  /* Reserve an estimated size for the skipped row. Without this the box
     collapses to 0 height off-screen, the scrollbar jumps, and you get
     layout shift (CLS). Tune the height to your typical row.
     Syntax: <width> <height>; use 'auto none' to let the browser
     remember the last rendered size. */
  contain-intrinsic-size: auto 120px;
}

Make it yours

Use the controls beside the demo above to change technique, page sections, and emphasize — each change updates the example live.

Experiment in the playground
  • Step through all four techniques and notice the trade: the more the browser is allowed to skip, the more rules you have to honor (reserve a size, understand the containment values).
  • For content-visibility, imagine dropping the contain-intrinsic-size line — that's the version that ships with scrollbar jank and layout shift.
  • Take a real long list in your product, add content-visibility: auto plus a contain-intrinsic-size estimate, and measure first render before and after.

Reproduce it with an LLM

Reproduce it with an LLM

You are a browser-performance engineer. A page renders a long feed of thousands of similar cards and scrolling is slow because the browser styles, lays out, and paints every card even off-screen. Use CSS containment and isolation to fix it: apply `content-visibility: auto` with a matching `contain-intrinsic-size` to skip rendering off-screen cards (and explain how that reserves space to avoid scroll-jank/CLS), discuss `contain: layout paint` to isolate each card's work, and cover `will-change`/layer promotion with its memory cost and why it must be used sparingly. Note the accessibility caveat: `content-visibility: hidden` content is removed from the a11y tree and in-page find, while `auto` is found when scrolled near. Return the CSS plus a short explanation of what each declaration isolates.

Pitfalls & accessibility

  • contain: paint and contain: size clip and constrain the box — make sure intentional overflow (tooltips, focus rings, dropdowns) isn't being silently cut off, and never leave will-change on permanently, since each promoted layer costs memory.

Further reading