Sticky header

Шапка сайта остаётся видимой при скролле; при прокрутке вниз добавляется тень и уменьшается высота.

Задача

Нужна шапка, которая фиксируется вверху страницы при скролле. Бонус: при прокрутке она визуально «уменьшается» и получает тень для отделения от контента.

Решение

<header class="header" id="header">
  <div class="header__inner">
    <a class="header__logo" href="/">Logo</a>
    <nav class="header__nav">
      <a href="#about">О нас</a>
      <a href="#services">Услуги</a>
      <a href="#contact">Контакт</a>
    </nav>
  </div>
</header>

<main style="margin-top: 70px;">
  <!-- контент -->
</main>
.header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: #fff;
  transition: box-shadow 0.3s, padding 0.3s;
}

.header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  max-width: 1200px;
  margin: 0 auto;
  padding: 16px 24px;
  transition: padding 0.3s;
}

.header.scrolled {
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}

.header.scrolled .header__inner {
  padding-top: 8px;
  padding-bottom: 8px;
}

.header__logo { font-size: 1.3rem; font-weight: 700; text-decoration: none; color: #1e293b; }

.header__nav { display: flex; gap: 24px; }
.header__nav a { text-decoration: none; color: #64748b; font-size: 0.95rem; }
.header__nav a:hover { color: #1e293b; }
const header = document.getElementById('header');

const observer = new IntersectionObserver(
  ([entry]) => {
    // scrolled когда шапка касается верха (не видно пространства над ней)
    header.classList.toggle('scrolled', !entry.isIntersecting);
  },
  { rootMargin: '-1px 0px 0px 0px', threshold: 0 }
);

// Наблюдаем за сигнальным элементом в самом верху страницы
const sentinel = document.createElement('div');
document.body.prepend(sentinel);
observer.observe(sentinel);

Ключевые моменты

  • position: sticky; top: 0 — нативный способ без JS; шапка прилипает к верху при скролле.
  • IntersectionObserver на sentinel-элемент — производительнее, чем слушатель scroll; не блокирует main thread.
  • z-index: 100 — чтобы шапка перекрывала контент при скролле.
  • margin-top на main не нужен при sticky (в отличие от fixed); добавляй только если шапка position: fixed.

Варианты

  • Hide on scroll down, show on scroll up (паттерн autohide header):
    let prevY = 0;
    window.addEventListener('scroll', () => {
      const curr = window.scrollY;
      header.classList.toggle('hidden', curr > prevY && curr > 100);
      prevY = curr;
    }, { passive: true });
    
    .header { transition: transform 0.3s; }
    .header.hidden { transform: translateY(-100%); }
    

Связанные рецепты / темы