Почему не работает position: sticky — 3 причины и как найти свою за минуту

Симптом
Ты пишешь position: sticky шапке или боковой колонке. Прокручиваешь страницу — а блок уезжает вместе со всем остальным, будто ты ничего не писал.
В консоли тишина. Ошибки нет и не будет: если прилипнуть не выходит, браузер молча ведёт себя так, будто там position: relative.
Сначала исключи общие причины, из-за которых не применяется вообще любой CSS: кэш, перебитое правило, опечатка. Их мы разобрали в статье почему не работает CSS. Проверь в DevTools: если на вкладке Computed у элемента стоит position: sticky, стиль дошёл. Значит, дело в одной из трёх причин ниже.
Как sticky работает на самом деле
Два правила, без которых причины не понять.
- Липкий блок держится только внутри своего родителя. Он прилипает, пока родитель на экране, и уезжает вместе с ним, когда родитель заканчивается.
- Прилипает он к ближайшему предку с прокруткой. Не к окну браузера, а к первому родителю с
overflow: hidden,autoилиscroll. MDN уточняет: даже если этот родитель на деле ничего не прокручивает.
Почти всегда сломан не сам блок, а кто-то из его родителей.
Причина 1. Не задан top
Самая частая и самая обидная. Одного position: sticky мало: браузеру нужно знать, где прилипать. MDN пишет прямо: без top, bottom, left или right липкий блок не отличить от обычного relative.
Как проверить. DevTools, вкладка Computed, свойство top. Если там auto — это оно.
Как починить.
.header {
position: sticky;
top: 0;
}
Если вёрстка на Tailwind, это два класса: sticky top-0. Второй забывают чаще всего.
Причина 2. У родителя стоит overflow
Где-то выше по дереву у одного из родителей написано overflow: hidden или overflow: auto. Частый случай — overflow-x: hidden на body или на общей обёртке. Его ставят, чтобы убрать горизонтальную прокрутку на телефоне, и заодно ломают все липкие блоки внутри.
По второму правилу блок теперь прилипает к этой обёртке. Но прокручивается окно, а не обёртка. Вот он и уезжает вместе с ней.
Второй частый случай — таблица. Липкие заголовки столбцов делают так: th { position: sticky; top: 0; }. А саму таблицу для телефона кладут в обёртку с overflow-x: auto, чтобы её можно было листать вбок. Обёртка перехватывает прилипание, и заголовки уезжают вместе со строками. Здесь clip не поможет — прокрутка вбок нужна. Реши, что важнее, или задай обёртке высоту, чтобы вертикально прокручивалась она сама.
Как проверить. Выдели липкий блок на вкладке Elements, открой консоль и выполни:
let el = $0;
while ((el = el.parentElement)) {
const s = getComputedStyle(el);
const scrolls = (v) => v !== 'visible' && v !== 'clip';
if (scrolls(s.overflowX) || scrolls(s.overflowY)) {
console.log(el, s.overflowX, s.overflowY);
}
}
$0 в консоли — это элемент, который ты выделил в Elements. Скрипт выведет всех родителей, которые перехватывают прилипание.
Как починить. Убери overflow у найденного родителя. Если он нужен, чтобы срезать то, что вылезает за край, замени hidden на clip:
body {
overflow-x: clip; /* было: hidden */
}
clip тоже обрезает лишнее, но, по MDN, не превращает блок в контейнер прокрутки. Работает в Chrome с 90-й версии, Firefox с 81-й и Safari с 16-й. А откуда на телефоне вообще берётся горизонтальная прокрутка, разбирали в гайде как сделать сайт адаптивным.
Причина 3. Блоку некуда ехать
Липкий блок двигается только внутри родителя — это первое правило. Если родитель ровно такой же высоты, как сам блок, ехать некуда. Sticky формально работает — на ноль пикселей.
Два типичных случая:
- Лишняя обёртка. Шапка лежит внутри
<div>её же высоты. Так часто бывает с компонентами:<div class="header-wrap">вокруг<header>. Липким нужно делать внешний блок — тот, что лежит прямо в длинном контейнере. - Колонка во flex или grid. Боковая панель стоит рядом с контентом внутри
display: flex. По умолчанию flex растягивает её на всю высоту ряда, и ей снова некуда ехать.
Как проверить. Наведи курсор на родителя в Elements. Если его подсветка по высоте совпадает с самим блоком — это третья причина.
Как починить.
.sidebar {
position: sticky;
top: 16px;
align-self: flex-start; /* в grid — align-self: start */
}
align-self отменяет растягивание. Колонка становится высотой со своё содержимое, и у неё появляется запас, чтобы ехать.
Порядок проверки
- На вкладке Computed стоит
position: sticky? Нет — сначала обычные CSS-причины. - У
topзначение неauto? Еслиauto— причина 1. - Скрипт нашёл родителя с
overflow? Да — причина 2. - Родитель выше самого блока? Нет — причина 3.
Короткие уроки-истории, симулятор агента и ежедневная практика — в нашем мобильном приложении. Бесплатно.
Почему sticky работает в Chrome, но не в Safari?
В современных браузерах разница почти всегда не в браузере, а в одной из трёх причин. Префикс -webkit-sticky нужен только Safari старше 13-й версии.
Чем sticky отличается от fixed?
fixed вынимается из потока и всегда прикреплён к окну: контент под ним начинается так, будто шапки нет. sticky занимает своё место в потоке и прилипает, только пока его родитель на экране.
Почему при переходе по якорю заголовок прячется под шапку?
Браузер прокручивает заголовок к самому верху окна, а там уже липкая шапка. Добавь html { scroll-padding-top: 64px; } и подставь высоту своей шапки.





