Меню должно оставаться у верхнего края, но уезжает вместе со страницей. Найдём причину по дереву элементов: проверим ось прилипания, прокрутку предков и свободное место внутри контейнера.

Sticky остаётся внутри родительского блокаЭлемент удерживается у top, пока не достигнет нижней границы родителя.До прилипанияtopstickyЭлемент у toptopstickyРодитель заканчиваетсяtopstickyНиже границыуйти нельзяУ предков также проверьте overflow и доступную высоту для движения.
У sticky есть предел движения: нижняя граница родительского блока. Отдельно проверьте overflow у предков и высоту контейнера.

Сначала определите, что должно прокручиваться

Для диагностики нужны редактор, браузер и базовые знания HTML и CSS. Пример работает как локальная страница без JavaScript. Сначала восстановим ожидаемое поведение, затем намеренно сломаем его несколькими способами.

У sticky два ориентира. Отступ задаёт положение относительно области прокрутки, а содержащий блок ограничивает перемещение элемента. Поэтому меню может держаться у верхнего края и позже уехать вместе с концом своей секции. Это ожидаемая граница. Правила закреплённого позиционирования в спецификации CSS.

Перед правкой ответьте на конкретный вопрос: меню должно следовать за прокруткой всего документа или внутренней панели? Мы рекомендуем сначала назвать эту область. Замена overflow у всех родителей наугад меняет поведение страницы и часто скрывает исходную причину.

Основные значения position разобраны в статье о позиционировании. Здесь работаем с диагностикой sticky по вертикали в обычном горизонтальном режиме письма.

Создайте пример с видимым концом контейнера

Сохраните разметку в index.html, а стили из следующего блока — в styles.css. Большие секции нужны только для учебной прокрутки.

<!doctype html>
<html lang="ru">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Диагностика sticky</title>
    <link rel="stylesheet" href="styles.css">
  </head>
  <body>
    <header class="page-header">Учебная страница</header>
    <div class="viewport">
      <main class="layout">
        <nav class="menu" aria-label="Разделы статьи">
          <a href="#start">Начало</a>
          <a href="#middle">Середина</a>
          <a href="#end">Конец</a>
        </nav>
        <article class="content">
          <h1>Проверяем закреплённое меню</h1>
          <section id="start">
            <h2>Начало</h2>
            <p>Прокрутите страницу до следующей секции.</p>
          </section>
          <section id="middle">
            <h2>Середина</h2>
            <p>Следите за верхним краем меню.</p>
          </section>
          <section id="end">
            <h2>Конец</h2>
            <p>Меню ограничено высотой этого блока.</p>
          </section>
        </article>
      </main>
    </div>
    <footer class="page-footer">Область после статьи</footer>
  </body>
</html>

Добавьте стили:

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font: 18px/1.5 system-ui, sans-serif;
}

.page-header,
.page-footer {
  padding: 2rem;
  background: #e8e8e8;
}

.page-footer {
  min-height: 20rem;
}

.layout {
  display: grid;
  grid-template-columns: 14rem minmax(0, 1fr);
  gap: 2rem;
  max-width: 68rem;
  margin-inline: auto;
  padding: 1rem;
  border: 2px solid #567;
}

.menu {
  position: sticky;
  top: 1rem;
  align-self: start;
  display: grid;
  gap: 0.75rem;
  padding: 1rem;
  border: 2px solid #222;
  background: #fff;
}

.content {
  min-width: 0;
  overflow-wrap: anywhere;
}

.content section {
  min-height: 32rem;
  padding-block: 1rem;
  border-bottom: 1px solid #999;
}

@media (max-width: 45rem) {
  .layout {
    grid-template-columns: minmax(0, 1fr);
  }

  .menu {
    position: static;
  }
}

Проверяйте широкое окно, больше 45rem: на узком экране пример намеренно переводит меню в обычный поток. Прокрутите документ. Когда меню достигнет отступа 1rem сверху, оно должно удерживать это положение. Ближе к нижней границе .layout закрепление закончится; футер помогает увидеть этот момент.

У sticky нет отдельного переключателя «работает». Иногда элемент ещё не дошёл до порога, иногда уже дошёл до границы контейнера. Проверяйте обе точки.

Посмотрите на отступ именно по нужной оси

Удалите top: 1rem у меню и повторите прокрутку. При значениях top: auto и bottom: auto смещение по вертикальной оси не задаётся. Один position: sticky этого поведения не включает. Описание position на MDN.

Верните top. При горизонтальном прилипании проверяют left или right и горизонтальную прокрутку. Логические свойства, например inset-block-start, зависят от режима письма; выбирайте их, когда интерфейс действительно должен следовать направлению текста.

Откройте вкладку Computed в инструментах разработчика. Убедитесь, что конечное значение position — sticky, а нужный отступ не равен auto. Правило из другого медиавыражения может переопределить ваш CSS. В нашем примере это происходит преднамеренно на узкой ширине.

Найдите предка, который перехватил прокрутку

Добавьте в конец CSS:

.viewport {
  overflow: hidden;
}

Теперь сравните движение меню. У предка появился механизм прокрутки, хотя полосы прокрутки не показаны и пользователь прокручивает весь документ. Такая обёртка способна стать ориентиром для sticky, и ожидаемого удержания у края окна вы не увидите.

Проверять нужно всю цепочку до корня. В инструментах разработчика выберите меню, затем поднимайтесь по родителям и смотрите вычисленные overflow-x и overflow-y, размеры и реальное содержимое. CSS у ближайшего предка может быть чистым; причина найдётся выше.

В Chrome DevTools цепочку удобно вывести в консоль одним фрагментом кода, чтобы сравнить размеры всех родителей рядом с их overflow. Сначала выделите меню во вкладке Elements: $0 обозначает выбранный элемент.

const rows = [];

for (let node = $0; node; node = node.parentElement) {
  const style = getComputedStyle(node);

  rows.push({
    element: node.tagName + "." + node.className,
    overflowX: style.overflowX,
    overflowY: style.overflowY,
    clientHeight: node.clientHeight,
    scrollHeight: node.scrollHeight
  });
}

console.table(rows);

Это вспомогательная проверка, а не автоматический вердикт. По таблице вы увидите кандидата, после чего проверите прокрутку и положение элемента на странице. Корневой элемент и body имеют особенности, поэтому не выводите правило для всех макетов по одному числу scrollHeight.

Обратите внимание на сочетание осей. Например, заданный overflow-x: hidden может изменить вычисленное значение второй оси с visible на auto. Читайте обе колонки Computed. Значения и вычисление overflow.

Выберите исправление под назначение обёртки

Если внутренняя панель действительно должна прокручиваться, создайте ей ограниченную высоту. Замените последнее правило на:

.viewport {
  height: 24rem;
  overflow: auto;
}

Прокручивайте эту панель, поставив указатель над ней. Меню теперь ориентируется на её область прокрутки. Колесо над футером продолжит прокручивать документ. Получились две разные области; это осмысленный сценарий, если так устроен интерфейс.

Если overflow: hidden использовали только ради декоративного обрезания, рассмотрите overflow: clip. Оно обрезает содержимое и не создаёт контейнер прокрутки. Но clip запрещает также программную прокрутку и само по себе не создаёт новый блочный контекст форматирования. Если обёртке нужен отдельный блочный контекст, проверьте display: flow-root: это отдельное решение, у которого есть собственные последствия для раскладки.

Проверьте, что обрезание не спрятало ссылки, рамки фокуса или часть самого меню. Смешивание clip с auto либо scroll по другой оси тоже требует проверки вычисленных значений. Универсального исправления одним overflow: visible здесь нет: внутренней панели оно уберёт нужную прокрутку.

Для возврата к исходному упражнению удалите дополнительное правило .viewport целиком.

Проверьте высоту и растяжение в сетке

Уберите align-self: start у меню. В Grid элементы по умолчанию часто растягиваются на высоту своей области; в нашем примере меню займёт высоту соседней статьи. Для заметного движения внутри той же области места почти не останется.

Посмотрите рамку меню. Если она тянется до конца всех секций, проблема видна без прокрутки. Верните align-self: start, чтобы высота меню зависела от его содержимого. Другой вариант — растянутая боковая колонка с небольшим закреплённым элементом внутри; тогда sticky назначают внутреннему элементу.

Похожая ситуация возникает, когда меню находится в обёртке, высота которой равна самому меню. Меню может двигаться только внутри содержащего блока. Увеличивать высоту произвольным числом ради эффекта в рабочем макете не стоит: сначала проверьте, в какую секцию действительно входит меню.

Слишком высокое меню — отдельная проблема. Размер больше области прокрутки не означает автоматическое отключение sticky: спецификация описывает и такой случай. Но пользователю может быть трудно добраться до нижних пунктов. На низком экране лучше менять компоновку или тщательно проектировать отдельную прокрутку меню.

Пройдите диагностику с одной поломкой за раз

НаблюдениеЧто проверить
Меню уезжает с самого началаВычисленные position и отступ по нужной оси.
Закрепление меняется после overflow у обёрткиБлижайшую область прокрутки и обе оси overflow.
Меню сразу занимает высоту статьиРастяжение Grid и align-self.
Меню уезжает у конца секцииГраницу содержащего блока; это может быть ожидаемо.
Нижние ссылки недоступны на низком экранеВысоту меню и доступность всех пунктов.

Восстановите исходный пример и снова прокрутите его до футера. Затем по отдельности воспроизведите три поломки: уберите отступ, добавьте overflow: hidden, удалите align-self. Для каждой запишите причину и верните рабочее правило перед следующей проверкой.

Самостоятельное продолжение — добавить фиксированную шапку. Настройте верхний отступ так, чтобы меню не заходило под неё, и проверьте страницу при увеличении масштаба до 200%. Ссылки должны оставаться видимыми и доступными с клавиатуры.

Примените диагностику к адаптивной странице

После учебных поломок найдите закреплённый элемент в своём макете и подпишите его область прокрутки и ограничивающий блок. С этим упражнением переходите к программе курса «Адаптивная вёрстка»: сверяйте входные требования и выбирайте дальнейшую практику с разными ширинами и высотой окна.


«Доктайп» — журнал о фронтенде. Читайте, слушайте и учитесь с нами.

ТелеграмПодкастБесплатные учебники