scrollbar-gutter: Preventing Layout Shifts Caused by Scrollbars
AI generated
{ }
@
CSS · Layout · Scroll · Web Performance
scrollbar-gutter
Reserve space for the scrollbar before it even appears

A scrollbar that suddenly appears or disappears shifts the entire visible content by its own width, because under classic overflow: auto it shrinks the available content area. scrollbar-gutter: stable reserves that space permanently, whether the bar is currently visible or not, eliminating one of the most stubborn layout shift bugs on the web.

12 min read scrollbar-gutter · overflow Chrome · Firefox · Safari (current)

1. Why an appearing scrollbar triggers a layout shift

Under the classic overflow: auto rule, the browser only shows a scrollbar once the content actually exceeds the container's height. The problem: once the bar appears, it takes up real space inside the container on many operating systems, shrinking the available content width and causing every element inside to potentially wrap or shift differently. That exact effect shows up frequently when a dropdown menu opens, when content loads via infinite scroll, or when switching between two views with different content lengths.

For users, a jump like that reads as a small stutter that reinforces the impression of an unstable or unfinished page, and for search engines it counts directly toward Cumulative Layout Shift (CLS), one of the three Core Web Vitals. A layout that rewraps every time a scrollbar appears therefore degrades not just the perceived, but also the measurable quality of the page.

2. scrollbar-gutter: stable permanently reserves space

The scrollbar-gutter property solves this by reserving the area a scrollbar would occupy, independent of whether the bar is currently visible. With the value stable, the browser keeps that space free permanently, whether the content is currently overflowing or not, so the scrollbar appearing or disappearing no longer changes the content area's width. The default value is auto, which matches the classic, layout shifting behavior.

The reserved area is called the gutter and behaves like an extra, invisible margin on the relevant edge of the container. Even when no scrollbar is displayed, that margin stays as empty space, which at first glance looks like wasted room but is actually exactly the price a consistently stable layout costs.


/* Permanently reserve space for the scrollbar */
.scroll-container {
  overflow-y: auto;
  scrollbar-gutter: stable;
  max-height: 60vh;
}

3. both-edges: reserving symmetric space on both sides

An additional value, both-edges, combined with stable, reserves the same space not only on the side where the scrollbar actually appears, but mirrored on the opposite side as well. This is especially helpful for centered content that should visually stay exactly in the middle of the viewport: without both-edges, a scrollbar appearing on the right would nudge the content slightly to the left, because space is only reserved on one side.

In practice, both-edges fits mainly modal dialogs or centered layout containers, where a visual shift is noticed immediately, while for regular page content, whose left edge is already aligned to the viewport edge anyway, plain stable is usually entirely sufficient.


/* Centered modal content stays visually centered,
   even when a scrollbar appears on the right */
html {
  scrollbar-gutter: stable both-edges;
}

4. Differences between operating systems: overlay vs. classic scrollbars

How noticeable a layout shift is at all depends heavily on the operating system and its scrollbar style. macOS shows a semi transparent overlay scrollbar by default, which floats over the content and takes up no space, meaning the classic problem often does not show up at all under many configurations. Windows and most Linux desktop environments, on the other hand, show a classic, space consuming scrollbar that actually shrinks the available area.

These differences make scrollbar-gutter: stable a safeguard that often stays invisible on macOS but makes a noticeable difference on Windows and Linux. Anyone testing only on a macOS device can easily miss the problem, which is why testing under Windows, or with a forced classic scrollbar style, is recommended before launching a new layout.

5. Combining with overflow: when scrollbar-gutter actually takes effect

scrollbar-gutter only affects elements that are potentially scrollable, meaning they have an overflow value other than visible, usually auto, scroll, or hidden combined with additional scrolling controlled through JavaScript. On an element with overflow: visible, the property has no effect, because such an element by definition never shows a scrollbar of its own.

Most commonly, scrollbar-gutter is used together with overflow-y: auto on the html or body element, to stabilize the global page scrollbar, but the combination works exactly the same on individual containers like a sidebar menu, a scrollable modal body, or a long product variant list that scrolls independently of the rest of the page.


/* The global page scrollbar gets reserved space,
   so switching between short and long pages
   no longer changes the content width */
html {
  overflow-y: auto;
  scrollbar-gutter: stable;
}

6. A typical bug: modal opens, background content jumps sideways

A particularly common use case is opening a modal that sets overflow: hidden on the body element via JavaScript to prevent background scrolling. Without scrollbar-gutter, the page's scrollbar disappears completely at that moment, sliding the entire page content to the right by its width, visible as a short but jarring jump of the navigation, logo and content when the modal opens, and again when it closes.

With scrollbar-gutter: stable permanently set on the html element, the reserved space stays in place even while overflow: hidden is active and the bar itself becomes invisible, because the property keeps the space free regardless of the actual overflow value, as long as overflow is not visible. The jump on opening and closing modals disappears completely, with no JavaScript workaround measuring widths manually required.

7. Measuring layout shift: DevTools and the Web Vitals extension

Whether a scrollbar related layout shift is actually happening can be verified in Chrome DevTools through the Performance tab and the Layout Shift entries logged there, which show exactly the affected moment and the elements that shifted. The free Web Vitals browser extension additionally shows a page's current CLS value in real time and visually highlights which elements moved.

For a targeted test, it helps to load a page or list right above and right below the threshold where a scrollbar appears, for example by adding or removing a single list item, and watch whether elements outside the scrolling container visibly move. If the layout stays stable in both cases, scrollbar-gutter is working as intended.

8. Browser support and a harmless fallback for older browsers

Chrome, Edge and Firefox support scrollbar-gutter reliably in current versions, Safari caught up with a more recent version, so the property has by now arrived in every major browser engine. A browser that does not know the property simply ignores the declaration and falls back to the classic, layout shifting behavior, with no errors or unexpected side effects, because scrollbar-gutter is purely additive and does not override any other property.

That makes adoption risk free: there is no reason to wait for an @supports feature flag or a JavaScript detection before using scrollbar-gutter: stable in production. For users on a non-supporting browser, nothing changes compared to the already familiar previous behavior, while users on a supporting browser immediately benefit from a more stable layout.

9. Practical use in a shop: category pages, filter lists and checkout

In a Magento and Hyvä context, scrollbar-gutter: stable pays off particularly in three places: the global html level against jumps when opening cart or filter modals, scrollable filter lists in category navigation whose length varies depending on the selected filters, and scrollable areas in checkout, for example a long payment method or shipping option list, where a layout shift during a critical conversion step would be especially damaging.

Implementing it in Tailwind costs only a single utility class or a short line in the CSS layer and requires no extra JavaScript, no manual width measurement, and no adjustment to existing Alpine.js components. Given the low implementation effort against the noticeable gain in stability, scrollbar-gutter: stable belongs in practically every modern layout with potentially scrollable areas.

Value Reserved space When it makes sense Effect when the bar appears
auto (default) None When layout shift is tolerated Content width visibly shrinks
stable One side, permanent Scrollable containers, page scroll No shift, bar appears inside the gutter
stable both-edges Both sides, permanent Centered layouts, modals No shift, content stays exactly centered
Overlay scrollbar (macOS) No physical space needed System dependent, not controllable Usually no shift anyway

Mironsoft

Modern CSS, layout architecture and rendering performance

CSS that stays maintainable instead of breaking with every change?

We review existing stylesheets for specificity chaos and layout thrashing, then build a CSS architecture with cascade layers, custom properties and modern layout primitives that still makes sense after the tenth feature.

CSS Audit

Systematically uncovering specificity issues, cascade conflicts and unused selectors.

Architecture Refactoring

Introducing cascade layers, custom properties and design tokens cleanly.

Performance Tuning

Fixing layout thrashing, expensive selectors and rendering bottlenecks.

10. Summary

scrollbar-gutter: The Essentials at a Glance

Core idea

scrollbar-gutter: stable permanently reserves the space a scrollbar would take, independent of whether the bar is currently visible.

both-edges

stable both-edges reserves space on both sides and keeps centered content exactly centered even when the bar appears.

OS difference

macOS often uses space saving overlay scrollbars, Windows and Linux show classic, space consuming bars, so testing on multiple systems matters.

Prerequisite

scrollbar-gutter only works together with overflow other than visible, usually auto, scroll, or JavaScript controlled hidden.

11. FAQ: scrollbar-gutter: The Essentials at a Glance

1What exactly does scrollbar-gutter: stable do?
It permanently reserves the space a scrollbar would occupy, regardless of whether the bar is currently visible. That means the content width no longer changes when the bar appears or disappears.
2Why don't I see this problem on my Mac at all?
macOS shows a semi transparent overlay scrollbar by default, which floats over the content and takes up no space. Windows and most Linux environments show a classic, space consuming bar instead.
3What does the both-edges value do?
both-edges additionally reserves the same space on the opposite side, so centered content stays exactly centered even when a scrollbar only appears on one side.
4Do I also need to set overflow?
Yes. scrollbar-gutter only affects elements with an overflow value other than visible, usually auto or scroll, because only such elements can show a scrollbar of their own at all.
5Does scrollbar-gutter fix the modal jump problem?
Yes, when it's permanently set on the html element. The reserved space then stays in place even when a modal sets overflow: hidden on body via JavaScript and the bar becomes invisible.
6Does scrollbar-gutter affect Cumulative Layout Shift?
Yes, indirectly. It prevents the width change triggered by an appearing or disappearing scrollbar element, removing a common cause of a poor CLS score.
7Is scrollbar-gutter available in all current browsers?
Chrome, Edge and Firefox support the property reliably in current versions, Safari caught up in more recent versions. Older browsers simply fall back to the classic, harmless layout behavior.
8Doesn't stable visibly waste space when there's no bar?
Yes, a small margin stays visibly empty when no bar is currently showing. That space is the deliberate price of a layout that doesn't jump when the bar appears.
9At which level should I set scrollbar-gutter?
On html or body for the global page scrollbar, and directly on the relevant container with its own overflow for individual scrollable areas like filter lists or modal bodies.
10How do I test whether scrollbar-gutter is working correctly?
Load a page or list right above and right below the threshold where a scrollbar appears, then check in Chrome DevTools or the Web Vitals extension whether a layout shift gets logged.