Skip to content
R.

Why does a scrollable panel shift when the scrollbar appears?

Reserve gutter space for the scroll container so the panel keeps the same usable width before and after it starts overflowing.

About 6 min readComments

A scroll container can look stable until one more item arrives, the scrollbar appears, and the content area narrows by a few pixels. That small change is enough to move aligned controls, make a secondary column jump, or make a header stop lining up with the body beneath it. The bug is easy to miss because the layout seems correct in the non-overflowing state.

This article isolates that failure in a minimal fixture and shows why `scrollbar-gutter: stable` fixes the specific shift without changing the content itself. It also shows where that fix stops helping, especially when the problem is not the gutter but a different overflow rule or a deliberate `overflow: hidden` choice.

The important distinction is between the scroll container's visible border box and the content width the page can actually use. If those two widths are not the same before and after overflow starts, a layout that looks centered or aligned in the short-content state can appear to move for no obvious reason. The fix is valuable precisely because it changes the browser's reservation strategy rather than trying to patch each component that depends on the width.

Reproduce the shift in a simple panel

The easiest way to see the bug is to build a card with a fixed height, let its body scroll, and place one aligned element beside the scroll area. In the empty or short-content state, the panel looks fine. After the list grows, the browser reserves room for the scrollbar inside the content box, so the usable inline width becomes smaller.

That is the failure the reader should recognize. The layout did not become responsive in a useful way; it lost space at the exact moment the panel changed from non-scrolling to scrolling.

Broken panel state where the scrollable list has not yet overflowed and the adjacent control appears aligned
Broken state: the panel looks aligned before overflow, so the shift is easy to miss until the scrollbar appears.

SourcesMDN: scrollbar-gutter (opens a new tab)CSS Overflow Module Level 4 (opens a new tab)

Why the usable width changes

A scrollbar is not just decoration. In classic scrollbar modes, it consumes space that would otherwise belong to the scroll container's content box. When the element moves from not overflowing to overflowing, the content area can shrink immediately, which is why labels, buttons, and right-aligned content appear to jump.

That behavior is different from text wrapping or from a grid track that is simply too small. The same content can fit in both states, but the available inline space is not identical once the browser decides a scrollbar is needed.

The cause is easy to misdiagnose if the panel also contains long text or nested layout rules. If a label wraps, the page may still be overflowing for a completely different reason. This article keeps the content short enough that the only moving part is the scrollbar reservation itself, which is what makes the example useful as a diagnostic rather than as a general layout tutorial.

SourcesMDN: scrollbar-gutter (opens a new tab)CSS Overflow Module Level 4 (opens a new tab)

Reserve the gutter before overflow starts

The repair is one line: set `scrollbar-gutter: stable` on the element that actually scrolls. That tells the browser to reserve gutter space even before the element overflows, so the usable content width stays stable when the scrollbar eventually appears.

If you need the content centered in the remaining space on both sides, `both-edges` can reserve gutter space symmetrically. The basic article only needs `stable`, because the question here is why the panel shifts at all, not how to fine-tune every scrollbar placement.

The important implementation detail is that the property belongs on the element that scrolls, not on an ancestor that merely wraps the panel. If the wrong element gets the rule, the page can still jump because the browser reserves the gutter on the wrong box. That is one reason this kind of bug feels unpredictable in a larger codebase: the visible movement shows up where the width is consumed, but the cause is one level deeper.

panel.css / css
.panel {
  height: 18rem;
  overflow: auto;
  scrollbar-gutter: stable;
}

.panel__row {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
}

Repaired panel state where the scrollable list keeps the same usable width after the scrollbar appears
Fixed state: the scroll gutter is reserved, so the aligned controls stay in place after overflow starts.

SourcesMDN: scrollbar-gutter (opens a new tab)

The fix is not a substitute for other overflow decisions

A common reaction is to hide the overflow instead. That can remove the scrollbar shift, but it also changes the interaction model. The user can no longer scroll to the clipped content, so the panel is no longer the same component, only a less visible one.

The property also does not fix unrelated overflow bugs. If the real problem is a child that is too wide, a wrapped header that still pushes past the container, or the wrong element receiving scroll behavior, the gutter reservation is not enough.

That distinction matters for teams that try to treat every visual shift as a scrollbar issue. The symptom may be a jump, but the repair depends on whether the browser is reserving space, clipping content, or just being asked to render a layout that cannot fit. `scrollbar-gutter` only solves the first case.

SourcesCSS Overflow Module Level 4 (opens a new tab)

Verify the boundary, not just the happy path

The local check should use the same viewport, the same content, and the same scale for the broken and fixed states. That proves the shift came from scrollbar appearance, not from a different layout size or from a changed fixture.

The geometry test was rerun locally with Chrome 153.0.8010.37, headless, on Windows x64 at 1280 by 900 CSS pixels and device scale factor 1. It checks the width difference between auto and stable gutters before overflow, then checks that the stable gutter preserves width and the Export control position after adding items. It does not assert that an auto gutter shifts on every platform. The independent Linux CI rerun records its own browser version and environment in the review evidence.

That is why the article does not promise a universal visual failure. The controlled example proves the geometry relationship the property controls, and the narrative explains where a reader still needs to inspect their own platform. A local pass is evidence for the mechanism, not for every scrollbar implementation on every operating system.

  1. Run the controlled example

    Open the fixture at one fixed viewport, then compare the list before and after adding enough items to create overflow.

  2. Check geometry, not just the screenshot

    Assert that the aligned control keeps the same x-position after overflow in the repaired version, and that the broken version moves.

  3. Record the negative case

    Keep a note that hiding overflow or changing the wrong element does not prove the same fix.

SourcesMDN: scrollbar-gutter (opens a new tab)

Share LinkedIn Email Subscribe

Discussion

Leave a comment

Comments appear after review. No email needed.

Image preview

Follow the blog

New articles in your feed. No email needed.

Use OpenRSS

Preview the feed, then choose a reader to subscribe.

Open in OpenRSS (opens a new tab)

Already have a reader?

Paste this link into your reader's Add feed option.

Get article summaries in your reader, not your inbox. View XML feed