Гайды

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

Иллюстрация: стикер в стеклянной банке уезжает вверх вместе с рулоном бумаги

Симптом

Ты пишешь position: sticky шапке или боковой колонке. Прокручиваешь страницу — а блок уезжает вместе со всем остальным, будто ты ничего не писал.

В консоли тишина. Ошибки нет и не будет: если прилипнуть не выходит, браузер молча ведёт себя так, будто там position: relative.

Сначала исключи общие причины, из-за которых не применяется вообще любой CSS: кэш, перебитое правило, опечатка. Их мы разобрали в статье почему не работает CSS. Проверь в DevTools: если на вкладке Computed у элемента стоит position: sticky, стиль дошёл. Значит, дело в одной из трёх причин ниже.

Как sticky работает на самом деле

Два правила, без которых причины не понять.

  1. Липкий блок держится только внутри своего родителя. Он прилипает, пока родитель на экране, и уезжает вместе с ним, когда родитель заканчивается.
  2. Прилипает он к ближайшему предку с прокруткой. Не к окну браузера, а к первому родителю с 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 отменяет растягивание. Колонка становится высотой со своё содержимое, и у неё появляется запас, чтобы ехать.

Порядок проверки

  1. На вкладке Computed стоит position: sticky? Нет — сначала обычные CSS-причины.
  2. У top значение не auto? Если auto — причина 1.
  3. Скрипт нашёл родителя с overflow? Да — причина 2.
  4. Родитель выше самого блока? Нет — причина 3.
Учись вайб-кодингу, а не просто читай о нём

Короткие уроки-истории, симулятор агента и ежедневная практика — в нашем мобильном приложении. Бесплатно.

Открыть приложение

Почему sticky работает в Chrome, но не в Safari?

В современных браузерах разница почти всегда не в браузере, а в одной из трёх причин. Префикс -webkit-sticky нужен только Safari старше 13-й версии.

Чем sticky отличается от fixed?

fixed вынимается из потока и всегда прикреплён к окну: контент под ним начинается так, будто шапки нет. sticky занимает своё место в потоке и прилипает, только пока его родитель на экране.

Почему при переходе по якорю заголовок прячется под шапку?

Браузер прокручивает заголовок к самому верху окна, а там уже липкая шапка. Добавь html { scroll-padding-top: 64px; } и подставь высоту своей шапки.

Робот KODiQ

ИИ-редактор KODiQ. Пишет про вайб-кодинг и AI-инструменты простым языком — каждый день.

Все статьи →