Ship content-visibility: auto to a Chrome 89 user and every heading below the fold leaves the accessibility tree. The screen reader gets a page that stops at the viewport. Chrome 90 closed the hole. The property is worth the version floor anyway, because it is the cheapest rendering win of 2021.
The name sells the wrong model. content-visibility reads like a visibility switch, so people reach for it as a politer display: none. It is containment machinery. The property did not start as CSS at all.
What the engine skips
Two declarations buy the win.
.section {
content-visibility: auto;
contain-intrinsic-size: 480px;
}content-visibility: auto applies layout, style and paint containment permanently. It adds size containment while the element sits off-screen. No style recalc inside, no layout inside, no paint. The engine does not paint skipped contents, and they skip hit-testing. Think visibility: hidden plus pointer-events: none. The element's own box still lays out. The content stays in the DOM, and find-in-page still works.
The spec triggers on "relevant to the user", and that phrase covers three conditions. The border box approaches the viewport. The element holds focus. The element holds a selection. So an off-screen element keeps rendering if the user has focus or selected text inside it. Tab order can defeat your optimization.
The size you have to guess
A skipped element has size containment, so it has no natural height. Without a hint, your scrollbar jumps around like it owes you money. contain-intrinsic-size is the placeholder the engine uses while the contents stay skipped. Eyeball your average section height and move on. The spec just gained an auto keyword so the engine remembers the last rendered size, but no engine parses it yet. Today you write the estimate by hand, and a wrong estimate shows up as scrollbar drift.
The numbers
The web.dev article by Una Kravets and Vladimir Levin measures a travel blog. Rendering time on load drops from 232ms to 30ms. Levin implemented the feature. A 7x win from one CSS rule. Profile your own page: DevTools, Performance tab, the purple layout blocks before and after.
The accessibility scar
For nine months the hole stayed open. Chrome 85 through 89 stripped skipped content from the accessibility tree, so headings and landmarks below the fold did not exist. Marcy Sutton documented it in December 2020. Chrome 90 fixed it last April, and skipped content now reaches the accessibility tree even though the engine never paints it.
The hidden variant
content-visibility: hidden is the other half of the API. It applies contain: style layout size and drops the content from rendering, but it keeps the cached rendering state. getBoundingClientRect() still answers. Toggling back is nearly free. display: none throws the state away and pays a full re-render.
The price: hidden content is invisible to every UA algorithm. Find-in-page misses it. Anchor navigation misses it. So the Display Locking folks experiment with hidden-matchable and a beforematch event. Find-in-page fires the event and your handler reveals the section. Collapsed sections you can Ctrl+F into. Watch that one.
Where to use it
- Long articles. Chunk by section, apply the rule to each.
- Feeds and lists. Each card gets the rule.
- Read-only tables, with the caveat below.
content-visibility is not a react-window killer. The DOM memory stays. The first scroll into a heavy skipped section pays its full render cost right there. It replaces windowing when the win you need is initial render.
Chromium only for now: Chrome, Edge, Opera, Samsung Internet. Firefox and Safari have not shipped it. It degrades to nothing, because browsers without support render everything, as they always did. Set your floor at Chrome 90 and ship the rule.