Skip to main content

Taming the Scrollbar Shift

Created: 2025-04-10
2 min read
CSS
Web Performance
Layout

A couple of months ago I noticed on my Windows computer that I noticed content layout shifts when navigating through this website. At first I did not understand what caused it, and more importantly, why. In the end it was because some pages would have enough content so a scrollbar would be shown while others did not and therefore switching between either would cause Content Layout Shift (CLS). That's the infamous scrollbar layout shift, and it's been driving developers crazy for years.

The Problem

On Windows, Chrome's scrollbars take up actual space in the viewport (unlike on macOS where they overlay content). When content becomes long enough to scroll, the scrollbar appears and poof - your layout shifts by about 17px. Buttons move, text reflows, and your pixel-perfect design goes out the window.

Solution 1: The Modern Way with scrollbar-gutter

CSS now has a property specifically designed for this problem:

CSS
body {
    scrollbar-gutter: stable;
}

This reserves space for the scrollbar even when it's not visible. Clean, simple, and elegant! But... browser support isn't perfect yet (though it's getting better). For Windows Chrome this would however always show the scrollbar even if there is nothing to scroll.

Another issue with this approach is that when you use Tailwind (you should!), it means quite a lot of changes to globals.css which is something I did not want to do just to resolve this issue.

Solution 2: The Math Approach

You can calculate and account for scrollbar width:

CSS
.container {
    width: calc(100% - 17px); /* Approximate scrollbar width */
    margin-right: auto;
    margin-left: 0;
}

This works but feels a bit hacky, and the exact scrollbar width can vary between operating systems and browsers.

Solution 3: The Tailwind CSS 4 Native Way

Since Tailwind CSS 4 is CSS-first, I no longer need a plugin or a separate Tailwind config file for this. A tiny utility in globals.css is enough to get the same result.

Here is the utility:

CSS
@utility scrollbar-none {
    scrollbar-width: none;
    -ms-overflow-style: none;
}

.scrollbar-none::-webkit-scrollbar {
    display: none;
}

My Take

I have found this to be the cleanest Tailwind approach. It keeps the implementation local to CSS, avoids extra dependencies, and applying the scrollbar-none class to the root layout is enough.