Because the browser is doing layout and paint work for content nobody has scrolled to yet. You compressed the images, deferred the scripts, and trimmed the CSS, and the page still stutters. The remaining cost is rendering, and it scales with how much content exists, not with how much is visible.
This shows up on the pages that matter most commercially. Long landing pages, documentation, blog archives, and product listings all pay this tax. The first screen looks fine in a screenshot, but scrolling feels heavy and interactions lag behind the finger.
CSS containment is the browser feature built for exactly this problem. It lets you tell the browser that a section of the page is self-contained, which means the browser can skip work on it until it matters. It is a small amount of CSS with an unusually large payoff, and most sites still do not use it.
CSS containment is a way to tell the browser that an element's contents are independent of the rest of the page. When the browser knows a section cannot affect anything outside itself, it can calculate and paint that section in isolation, or skip it entirely. That isolation is what makes the optimisation safe.
The mechanism is the contain property. MDN Web Docs defines its main values precisely. Layout containment means the internal layout of the element is isolated from the rest of the page, so nothing outside the element affects its internal layout and vice versa. Paint containment means descendants of the element do not display outside its bounds, so if the containing box is offscreen the browser does not need to paint its contained elements.
There are size and style values too, plus two shorthands. MDN describes contain: content as all containment rules except size, equivalent to layout paint style, and contain: strict as all of them together. In practice you rarely write these by hand any more, because a newer property does the useful work for you.
It skips rendering work for content the user cannot see. Google's web.dev describes the content-visibility property as enabling the user agent to skip an element's rendering work, including layout and painting, until it is needed. Set it to auto on your page sections and offscreen ones stop costing you anything.
The auto value is the one worth knowing. MDN states that with auto, the element turns on layout containment, style containment, and paint containment, and if the element is not relevant to the user, it also skips its contents. The browser does this work as the section approaches the viewport, so the reader never sees the difference.
There is also a hidden value, which behaves quite differently. MDN describes hidden as skipping contents that must not be accessible to user-agent features such as find-in-page and tab-order navigation, and says it is similar to giving the contents display: none. That is a tool for cached views you control in JavaScript, not something to sprinkle across a marketing page.
The gains are large enough to be worth a look on any long page. Google's web.dev article reports a travel blog demo where initial rendering time improved from 232 milliseconds to 30 milliseconds, roughly a seven times boost, and says you can expect a reduction of 50% or more from rendering costs.
The same article reports that Facebook engineers observed up to 250 milliseconds improvement in navigation times when using content-visibility: hidden for cached views. That is a different use case from the marketing page one, but it shows the ceiling on what this class of optimisation is worth when applied deliberately.
Be careful about what these numbers mean for you. They are rendering time improvements, not total load time improvements, and they scale with how much offscreen content you have. A short page with three sections will barely notice. A documentation page with two hundred blocks will feel like a different site. Measure your own before and after rather than assuming the demo number transfers.
It is the placeholder size the browser uses for a section it has not rendered yet. Without it, skipped sections collapse to nothing, your scrollbar jumps around, and the page height changes as you scroll. web.dev describes it as effectively specifying the natural size of the element if the element is affected by size containment.
This is not optional polish. If you apply content-visibility: auto without giving the browser a size estimate, you have traded a rendering problem for a layout stability problem, and layout stability is a Core Web Vital. The scroll experience gets visibly worse even though the rendering metric improves.
The good news is the browser can learn. web.dev notes that the auto keyword for contain-intrinsic-size lets browsers remember the last-rendered size and retain it, which prevents scrollbar shifts on infinite scrollers. So you provide a rough estimate, and the browser corrects itself once it has actually seen the section. If layout shift is unfamiliar territory, our guide to Cumulative Layout Shift explains why this matters to your scores.
Not when you use auto, and this is the question we get asked first. MDN is explicit: offscreen content within a content-visibility: auto property remains in the document object model and the accessibility tree, which allows improving page performance without negatively impacting accessibility.
The distinction between the two values is where the risk lives. With auto, MDN says the skipped contents must still be available as normal to user-agent features such as find-in-page and tab order navigation, and must be focusable and selectable as normal. With hidden, they must not be. So auto is safe for content you want found, and hidden is not.
For search and AI crawlers, the content is in the HTML either way, which is the part that actually matters. A crawler reading your page source sees the full article regardless of whether the browser painted it. The rendering skip is a browser-side optimisation, not a content decision, and it does not remove anything from the markup.
Long pages made of repeating blocks. Blog archives, documentation, product listings, changelogs, case study indexes, and long-form landing pages all fit. The pattern to look for is many sibling sections of similar structure, most of which are below the fold on first load.
Card grids are the clearest win. A listing page with sixty cards is doing sixty times the layout work of a page with one, even though a visitor sees maybe six. Wrapping each card or each row in a contained block means the browser handles what is visible and defers the rest until scrolling brings it close.
The pages that do not benefit are short ones and ones where almost everything is visible immediately. A hero, a value proposition, and a contact form has nothing meaningful to skip. Adding containment there just adds a property to your stylesheet and a line to your maintenance burden for no gain.
Yes for the contain property, and yes with a caveat for content-visibility. MDN lists contain as Baseline Widely available, noting it has been available across browsers since March 2022. That one is entirely safe to use with no fallback thinking required.
content-visibility is newer. MDN lists it as Baseline 2024, newly available, and states that since September 2024 the feature works across the latest devices and browser versions, while noting that some parts of the feature may have varying levels of support. That is a meaningful difference from widely available, and it is worth understanding before you ship.
In practice the risk is low because the failure mode is graceful. A browser that does not support content-visibility simply renders the page the way it always did, so older visitors get the old performance rather than a broken page. That is the ideal shape for a performance optimisation, and it is the reason we treat this as safe to adopt. Our explainer on Baseline and what those labels mean covers how to read these support statuses properly.
The three common mistakes are applying it too high up the tree, forgetting the intrinsic size, and using hidden when you meant auto. Each one produces a different kind of bug, and the first is the one that quietly breaks layouts.
Applying containment to a wrapper that holds the whole page defeats the purpose and can break sticky headers, dropdown menus, and anything that intentionally paints outside its parent. Paint containment clips descendants to the container, so a mega menu inside a contained block gets cut off. Apply it to sections, not to the page shell.
The other failure is measuring the wrong thing afterwards. Rendering improvements often will not show up in a simple load-time number, because the page was never slow to download. Look at interaction responsiveness and at how the page behaves during scroll instead. Our piece on Largest Contentful Paint explains which metrics move for which kind of fix.
If you have long pages with repeating sections, yes, and it is a small change. Add content-visibility: auto and a sensible contain-intrinsic-size to your repeating block class, test scrolling on a real phone, and check that nothing that should overflow is getting clipped. That is an afternoon of work.
If your pages are short, skip it. Performance work should follow measurement, and this particular optimisation only pays on pages with a lot of offscreen content. Across the 150 or more projects we have delivered, the studio average PageSpeed score sits at 98, and the way we protect that is by choosing the right fix for the actual bottleneck rather than applying every technique everywhere.
If you are looking at a heavy page and are not sure whether rendering is your problem, we are happy to take a look with you. Send us the URL and what feels slow about it, and we will tell you where the time is actually going. You can reach us at phoenix.studio, and we usually reply within 48 hours.
Tell us where you want to go. We'll tell you how we'd get you there.