Skipping the Work You Can't See: content-visibility in Production
content-visibility lets the browser skip layout and paint for off-screen content, while contain-intrinsic-size keeps the scrollbar honest. What auto skips and where the gains disappear.
A long page is mostly not on screen. content-visibility is the CSS property that delays rendering an element — including layout and painting — until it is needed.[11] That is the entire argument: the browser lays out, styles, and paints the whole document even when most of it sits thousands of pixels below the fold.[1][6] One declaration tells it to stop.
.article-section {
content-visibility: auto;
contain-intrinsic-size: auto 480px;
}The measured payoff on a deliberately heavy page is real — a chunked travel blog went from 232ms of rendering to 30ms on initial load.[6][7] That is a demo number, not a forecast you should quote to your team, but the direction holds, and the cost is one CSS rule instead of a virtualization dependency.
What auto Actually Switches On
content-visibility is built on CSS containment, the mechanism that isolates a subtree so the browser can compute it independently of the rest of the page.[3] The auto value turns on layout, style, and paint containment unconditionally; when the element is not relevant to the user, it additionally skips the contents entirely.[1]
"Not relevant to the user" is a proximity judgment, not a visibility test. The spec describes three states for an element with content-visibility: auto — close to the viewport, far from it, and proximity-not-yet-determined — and it suggests a 50% viewport margin as a reasonable default, so the browser can begin preparing before the element is visible.[8] That margin is why content is usually already rendered by the time you scroll to it, and why auto on above-the-fold content is a no-op rather than a bug.
The distinction that trips people up is hidden. content-visibility: hidden always skips the contents, and those skipped contents must not be reachable through find-in-page, tab order, selection, or focus — the spec calls this similar to display: none.[1]
auto is the opposite: skipped contents must still be available to find-in-page and tab order, and must remain focusable and selectable.[1] Off-screen content under auto stays in the document object model and the accessibility tree.[1][6] That is a rare case where a rendering optimization does not cost you accessibility.
The Scrollbar Is the Tax
The browser cannot know how tall a section it declined to lay out should be. Left alone, it treats the element as having no contents and collapses it.[2] Multiply that by a hundred cards and the document height is wrong, the scrollbar is wrong, and the page jumps as sections expand while you scroll.
contain-intrinsic-size is the fix: it "sets the size of an element that a browser will use for layout when the element is subject to size containment."[2] The auto keyword is the part worth understanding. It tells the browser to remember the real size once the element has rendered, then reuse that remembered value the next time the element scrolls off-screen.[2] The remembered value is not used while the children are actually being rendered — then the real length applies.[2] That is what lets you get away with an approximate placeholder rather than a precise one, because the estimate only governs the first pass.[2]
Where the Optimization Dies
The browser can skip rendering work only if nothing forces it to render. Any DOM API that forces layout on a skipped subtree — reading offsetHeight, scrollHeight, or getBoundingClientRect() — makes the browser lay that subtree out synchronously, and the savings evaporate.[6]
This is the failure mode that matters in practice, because the code responsible is usually not code you wrote for this feature. Scroll-position libraries, tooltip and popover positioning, and animation libraries that measure their targets all read geometry as a matter of course. The web.dev guidance is blunt: audit your code for these calls if you are using content-visibility for performance.[6] Chromium prints console messages when one of these APIs is called on a subtree of an element with content-visibility: hidden, which is a starting point — but the silent reads on auto subtrees are the ones that quietly eat the gain.
Restarting Work You Parked
An element with auto gets a signal when its skip state changes. The contentvisibilityautostatechange event fires on any element with content-visibility: auto when it starts or stops being relevant to the user and skipping its contents.[5] The event object exposes a skipped property that returns true if the user agent is skipping the element's rendering.[4]
The intended use is expensive work that should not run while nobody is watching — MDN's example starts and stops canvas drawing routines based on the flag, thereby conserving processing power.[5] One caveat deserves repeating: contents hidden from view remain semantically relevant to assistive technology, so this signal should not be used to skip significant semantic DOM updates.[4]
Support and Rollout Order
Chrome and Edge have had content-visibility since 85, Firefox since 125, and Safari since 18, and MDN's compat data marks the property as no longer experimental and on the standard track.[9] The companion event and its skipped property are newer, landing in Chrome 108, Firefox 125, and Safari 18.[10] The one asymmetry in the compat data is content-visibility: auto on Safari: the current entry is 26, and the 18-to-26 entry is flagged as a partial implementation, because skipped content was not findable via find-in-page.[9] That is a genuine regression to know about if your Safari users rely on Cmd+F, and it is fixed in the newer entry.
For a rollout, apply the property to repeated, self-contained, below-the-fold blocks — feed cards, comment threads, long article sections — rather than to the wrapper that positions them, and pair it with contain-intrinsic-size: auto <approximate height>.[2] Then measure a before-and-after performance trace instead of trusting the demo numbers.
Sources
[1] https://developer.mozilla.org/en-US/docs/Web/CSS/content-visibility — content-visibility - CSS | MDN
[2] https://developer.mozilla.org/en-US/docs/Web/CSS/contain-intrinsic-size — contain-intrinsic-size - CSS | MDN
[3] https://developer.mozilla.org/en-US/docs/Web/CSS/contain — contain - CSS | MDN
[4] https://developer.mozilla.org/en-US/docs/Web/API/ContentVisibilityAutoStateChangeEvent — ContentVisibilityAutoStateChangeEvent - Web APIs | MDN
[5] https://developer.mozilla.org/en-US/docs/Web/API/Element/contentvisibilityautostatechange_event — Element: contentvisibilityautostatechange event - Web APIs | MDN
[6] https://web.dev/articles/content-visibility — content-visibility: the new CSS property that boosts your rendering performance - web.dev
[7] https://developer.chrome.com/blog/new-in-chrome-85 — New in Chrome 85 - Chrome for Developers
[8] https://drafts.csswg.org/css-contain-2 — CSS Containment Module Level 2 - CSSWG Editor's Draft
[9] https://raw.githubusercontent.com/mdn/browser-compat-data/main/css/properties/content-visibility.json — browser-compat-data css/properties/content-visibility.json - MDN
[10] https://raw.githubusercontent.com/mdn/browser-compat-data/main/api/ContentVisibilityAutoStateChangeEvent.json — browser-compat-data api/ContentVisibilityAutoStateChangeEvent.json - MDN
[11] https://raw.githubusercontent.com/web-platform-dx/web-features/main/features/content-visibility.yml — web-features: content-visibility - web-platform-dx