Why my page jumps when a modal opens — and why you can't see it on a Mac

You built a modal. To stop the page underneath from scrolling, you put overflow: hidden on body. Checked it on your MacBook — perfect.
A day later a friend on Windows writes: "When I open the window, the whole site twitches to the right". You open it on yours — nothing twitches.
Here's why. On a Mac, the scrollbar by default overlays the content and takes no space. On Windows it sits at the side and eats roughly 15–17 pixels of width. overflow: hidden removes the scrollbar — and the page is suddenly that many pixels wider. Everything centred shifts right.
Half your users have the bug. And you physically can't see it.
How to reproduce it yourself
Before fixing, catch the bug on your own machine.
- On a Mac: System Settings → Appearance → "Show scroll bars" → "Always". Now the bar takes space, like on Windows.
- In any browser: open a page with less content than the screen and one with more. If a centred header logo "wanders" as you switch between them — same cause.
Cause 1: overflow: hidden removes the scrollbar
The most common case — the one above. A modal, a slide-out menu, a fullscreen gallery: anything that locks scrolling with overflow: hidden.
How to check. Open the modal and look at the right edge of the page under the backdrop. If the scrollbar vanished and the content moved — that's it.
How to fix — the modern way. Ask the browser to always keep room for the bar:
html { scrollbar-gutter: stable; }
Now space for the scrollbar is reserved even when there's none. Remove scrolling — the width doesn't change, nothing jumps. The property works in Chrome and Edge from 94, Firefox from 97 and Safari from 18.2.
If your layout is centred and symmetry matters, there's scrollbar-gutter: stable both-edges. It reserves the same strip on the left too, so the page's centre matches the window's centre. Plain stable is usually enough, but on a landing page with a logo dead centre the difference shows.
To see how much room the bar takes for a particular person, run this in the console: window.innerWidth - document.documentElement.clientWidth. On Windows at 100% scaling you'll get about 17; on a Mac, 0. Ask your friend to run it — and the "it doesn't jump for me" argument is over.
How to fix — for older browsers. Measure the bar's width and replace it with padding when the modal opens:
function lockScroll() {
const gap = window.innerWidth - document.documentElement.clientWidth;
document.body.style.overflow = "hidden";
document.body.style.paddingRight = gap + "px";
}
function unlockScroll() {
document.body.style.overflow = "";
document.body.style.paddingRight = "";
}
innerWidth is the window width including the bar, clientWidth without it. The difference is the bar's width. On a Mac with overlay scrollbars it's 0, so the code does no harm.
Cause 2: a fixed header doesn't know about the padding
You fixed body, but a header with position: fixed still twitches. That's because a fixed element is positioned against the window, not body. The body's padding means nothing to it.
How to check. The content stays put; only the header or a "Back to top" button jumps.
How to fix. Give it the same padding. Handy with a CSS variable:
document.documentElement.style.setProperty("--scrollbar-gap", gap + "px");
.header { padding-right: var(--scrollbar-gap, 0px); }
With scrollbar-gutter: stable on html, this usually goes away by itself: the window no longer changes width. If your header is sticky rather than fixed and misbehaves in other ways, see why position: sticky doesn't work.
Cause 3: the page jumps even without a modal
Sometimes the site twitches when moving between pages: the home page has lots of content and a scrollbar, the "Contact" page has little and no scrollbar. Full-width layouts and the centre "wander".
How to fix. The same scrollbar-gutter: stable on html. It keeps the space on every page.
Don't confuse this with sideways scrolling on a phone — that's a different cause, covered in why my page scrolls sideways on mobile.
Why not just always use overflow-y: scroll?
That works too: the bar is always visible, even on short pages, so nothing jumps. But an empty grey bar on a page that doesn't scroll looks like a bug. scrollbar-gutter reserves the space without the bar itself.
Does scrollbar-gutter change anything on a Mac?
With overlay scrollbars — no, there's nothing to reserve. So it's safe for everyone: it works where it's needed and stays quiet where it isn't.
Don't modal libraries already fix this?
Many do: they measure the bar themselves and set padding-right. But if you lock scrolling with your own code alongside a library, the padding can double. Make sure only one of them sets it.
Short story-lessons, an agent simulator and daily practice — in our mobile app. Free.


