Почему страница прыгает при открытии модального окна — и почему на Mac этого не видно

Ты сделал модальное окно. Чтобы страница под ним не прокручивалась, повесил на body стиль overflow: hidden. На своём MacBook проверил — всё идеально.
Через день пишет друг с Windows: «Когда открываю окно, весь сайт дёргается вправо». Ты открываешь у себя — ничего не дёргается.
Смотри, почему так. На Mac полоса прокрутки по умолчанию накладывается поверх контента и места не занимает. На Windows она стоит сбоку и съедает примерно 15–17 пикселей ширины. overflow: hidden убирает полосу — и страница внезапно становится шире на эти пиксели. Всё центрированное сдвигается вправо.
Баг есть у половины твоих пользователей. А ты его просто физически не видишь.
Как воспроизвести у себя
Прежде чем чинить, поймай баг на своей машине.
- На Mac: Системные настройки → Оформление → «Показывать полосы прокрутки» → «Всегда». Теперь полоса занимает место, как на Windows.
- В любом браузере: открой страницу, где контента меньше экрана, и где больше. Если шапка с логотипом по центру «гуляет» при переходе между ними — это та же причина.
Причина 1: overflow: hidden убирает полосу прокрутки
Самый частый случай — тот, что выше. Модалка, выдвижное меню, полноэкранная галерея: всё, что блокирует прокрутку через overflow: hidden.
Как проверить. Открой модалку и посмотри на правый край страницы под затемнением. Если полоса прокрутки пропала, а контент уехал — это она.
Как починить — современный способ. Попроси браузер всегда держать место под полосу:
html { scrollbar-gutter: stable; }
Теперь место под полосу зарезервировано, даже если её нет. Убрали прокрутку — ширина не изменилась, ничего не прыгает. Свойство работает в Chrome и Edge с версии 94, Firefox с 97 и Safari с 18.2.
Если вёрстка у тебя по центру и важна симметрия, есть вариант scrollbar-gutter: stable both-edges. Он резервирует такую же полосу и слева, и тогда центр страницы совпадает с центром окна. Обычно хватает просто stable, но для лендинга с логотипом ровно посередине разница заметна.
Узнать, сколько места занимает полоса у конкретного человека, можно прямо в консоли: window.innerWidth - document.documentElement.clientWidth. На Windows с масштабом 100% выйдет около 17, на Mac — 0. Попроси друга запустить это у себя — и спор «у меня не прыгает» закончится.
Как починить — для старых браузеров. Измерь ширину полосы и замени её отступом в момент открытия:
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 — ширина окна вместе с полосой, clientWidth — без неё. Разница и есть ширина полосы. На Mac с накладными полосами она будет 0, и код ничего не испортит.
Причина 2: фиксированная шапка не знает про отступ
Ты починил body, а шапка с position: fixed всё равно дёргается. Это потому, что фиксированный элемент считается от окна, а не от body. Отступ body ему не указ.
Как проверить. Контент стоит на месте, а прыгает только шапка или кнопка «Наверх».
Как починить. Добавь тот же отступ и ей. Удобно через CSS-переменную:
document.documentElement.style.setProperty("--scrollbar-gap", gap + "px");
.header { padding-right: var(--scrollbar-gap, 0px); }
Со scrollbar-gutter: stable на html эта проблема обычно уходит сама: окно больше не меняет ширину. Если шапка у тебя sticky, а не fixed, и ведёт себя странно иначе — загляни в разбор почему не работает position: sticky.
Причина 3: страница прыгает и без модалки
Бывает, что сайт дёргается при переходе между страницами: на главной контента много и есть полоса, на странице «Контакты» мало — полосы нет. Вёрстка шириной в 100% и центр «гуляют».
Как починить. Тот же scrollbar-gutter: stable на html. Он держит место всегда, на любой странице.
Не путай это с горизонтальной прокруткой, которая появляется у сайта на телефоне, — там другая причина, она разобрана в статье почему страница прокручивается вбок на мобильном.
Почему не написать overflow-y: scroll всегда?
Так тоже можно: полоса будет видна всегда, даже на коротких страницах, и прыжков не будет. Но пустая серая полоса на странице без прокрутки выглядит как баг. scrollbar-gutter резервирует место без самой полосы.
А scrollbar-gutter на Mac что-то меняет?
Если полосы накладные — нет, резервировать нечего. Поэтому свойство безопасно ставить всем: оно работает там, где нужно, и молчит там, где не нужно.
Библиотеки модалок это уже чинят?
Многие — да: они сами считают ширину полосы и ставят padding-right. Но если ты блокируешь прокрутку своим кодом рядом с библиотекой, отступ может удвоиться. Проверь, что его ставит кто-то один.
Короткие уроки-истории, симулятор агента и ежедневная практика — в нашем мобильном приложении. Бесплатно.


