How to Use contain-intrinsic-size to Stop Scrollbar Jumps

This guide is part of CSS Containment & content-visibility, within Rendering & CSS Performance. When content-visibility: auto skips rendering a section, the browser needs to know how much space that section takes; otherwise it would treat it as zero height. contain-intrinsic-size provides that placeholder size. If the placeholder is far from the real size, the page's total height changes as sections render during scrolling. The scrollbar thumb jumps, the page may lurch, anchor links land in the wrong place, and scroll-linked UI misbehaves.

Getting placeholder sizes right — and using the auto keyword so the browser remembers real sizes after rendering — removes nearly all of these problems while keeping the rendering savings.

Fixed estimate vs auto remembered size Comparison of a fixed contain-intrinsic-size value and the auto keyword that remembers last rendered size. Fixed estimate vs auto remembered size contain-intrinsic-size: 500px • Same placeholder before and after rendering • Height changes every time a section is skipped again • Scrollbar keeps jumping on scroll back contain-intrinsic-size: auto 500px • Estimate used only until first render • Real size remembered after rendering • Stable scrolling once sections are seen

Rapid Diagnosis

  • Watch the scrollbar thumb while scrolling a page with content-visibility. Jumps or size changes mean placeholder sizes are wrong.
  • Test anchor links to late sections; landing above or below the target indicates size drift above it.
  • Inspect sections in DevTools: compare the rendered height with the contain-intrinsic-size value.
  • Check for CLS reports while scrolling in lab tools with scroll interactions.

Root Cause Analysis

1. No intrinsic size. Skipped sections collapse to zero.

2. One estimate for all sections. Real heights vary widely between sections.

3. Fixed values without auto. Sections revert to the estimate when skipped again.

4. Responsive layouts. Heights differ greatly between mobile and desktop.

Step-by-Step Resolution

1. Always use auto with an estimate

css
.section {
  content-visibility: auto;
  contain-intrinsic-size: auto 800px;   /* auto = remember last rendered size */
}
/* trade-off: remembered sizes only exist after a section has rendered once, so
   the first scroll through the page still depends on the estimate. */

2. Use per-breakpoint estimates

css
.section { contain-intrinsic-size: auto 1400px; }        /* mobile: tall */
@media (min-width: 900px) { .section { contain-intrinsic-size: auto 800px; } }

3. Generate per-section estimates when sizes vary greatly

For content with very different section lengths, compute an estimate per section at build or render time (for example from word count and image count) and set it inline.

javascript
// Build step: estimate height from content, emit as a custom property.
const est = Math.round(120 + words * 0.42 + images * 360);   // px, mobile profile
html = `<section style="--est:${est}px" class="section">…</section>`;
// CSS: .section { contain-intrinsic-size: auto var(--est); }
// trade-off: the heuristic needs tuning per template; measure real heights once
// and fit the coefficients rather than guessing.

4. Use separate width and height when needed

contain-intrinsic-size accepts width and height (auto 300px auto 800px); for block-level sections only the height usually matters, but for inline-size-contained elements set both.

Scroll position error when jumping to section 20 Bar chart of how far an anchor link lands from its target with different intrinsic size strategies. Scroll position error when jumping to section 20 No intrinsic size 9400px One fixed estimate 1850px auto + per-breakpoint estimate 620px auto + per-section estimate 70px

Verification

Scroll from top to bottom quickly, then back up, watching the scrollbar thumb; it should remain nearly constant in size. Follow anchor links to the last sections and check they land at the target. In DevTools, compare the computed height of skipped sections (the estimate) with rendered heights. Record a scroll in the Performance panel with Layout Shift Regions enabled to confirm no unexpected shifts.

Worked Example: A Forum Thread Page

A forum rendered threads with up to 500 posts, applying content-visibility: auto to each post with contain-intrinsic-size: 200px. Posts varied from one line to several screens with images. Users reported that the scrollbar "kept moving" and that "jump to last unread" links landed in random places. The team switched to auto with an estimate computed per post from text length and attachment count, emitted as a CSS custom property. Anchor accuracy improved to within one post, scrollbar jumps disappeared after the first scroll, and the rendering savings stayed the same: thread pages kept a 70% reduction in initial layout time.

Lifecycle of a remembered size How an element with content-visibility auto and auto intrinsic size moves from estimate to remembered size. Lifecycle of a remembered size Skipped uses estimate Near viewport renders contents Size recorded last rendered height Skipped again uses remembered size

How auto Remembers Sizes

With auto, when an element with size containment from content-visibility has been rendered, the browser records its last rendered size. When it is skipped again, that recorded size is used instead of the estimate. This happens per element and lasts for the life of the element in the document. If the element is removed and recreated (common in client-rendered lists that re-render), the remembered size is lost. In frameworks, prefer stable keys so elements persist across updates. Remembered sizes also reset across navigations, which is why the initial estimate still matters for anchor links followed from other pages.

Measuring Real Sizes to Calibrate Estimates

Rather than guessing, measure. Load representative pages, let every section render (scroll to the bottom), and record each section's height with a ResizeObserver, along with simple features of its content: word count, number of images, number of code blocks. Fit a simple linear estimate from those features per template and breakpoint. Twenty or thirty pages per template are usually enough. Re-run the calibration when the design changes typography or spacing, since heights shift with every change to line height or margins.

Common Mistakes

  • Omitting auto. Sections snap back to the estimate when skipped again.
  • Tiny estimates. Make the document shorter than reality and push anchors upwards.
  • One size for all breakpoints. Mobile sections may be twice as tall as desktop ones.
  • Re-creating elements on every render. Remembered sizes are lost.

Edge Cases

Horizontal layouts. In horizontal scrollers, the width estimate matters; set both dimensions.

Images without dimensions inside sections. Real sizes change after images load; set image dimensions to keep remembered sizes accurate.

Dynamic content growth. Sections that expand (comments loading) update their remembered size after re-rendering.

Container queries. Size containment interacts with container queries; check that container-type and content-visibility are not on the same element unless intended.

FAQ

What does contain-intrinsic-size do?

It sets the size an element uses for layout when size containment applies — for example, when content-visibility: auto skips its contents.

What does the auto keyword do?

It tells the browser to remember the element's last rendered size and use it in place of the estimate after the element has been rendered once.

How accurate does the estimate need to be?

Within a factor of about two avoids most visible jumps on first scroll; per-section estimates are needed for accurate anchor links on very long pages.

Does a wrong estimate cause CLS?

Size changes of offscreen content generally do not count as layout shifts of visible elements, but they can move the scroll position and visible content in some cases. Accurate estimates minimise the risk.

Can I set contain-intrinsic-size without content-visibility?

Yes, with contain: size, but then the element always uses the given size. The common use is together with content-visibility: auto.

Is there a JavaScript API to read remembered sizes?

No direct API exists. You can measure rendered sizes with ResizeObserver and store them yourself if you need them across navigations.

Why do anchor links still land slightly off on first visit?

Sections above the target have not rendered yet, so the browser uses their estimates. Per-section estimates generated from content reduce this error.

Do I need different estimates for dark mode or font sizes?

Usually not for colour themes, but larger user font sizes make sections taller. Estimates in em or based on line counts scale better than fixed pixel values.