Реалізація Sticky Header/Footer на вебсайті
Sticky-елементи тривіальні через position: sticky, але правильна реалізація враховує напрямок прокрутки, продуктивність анімацій, safe-area на мобільних та конфлікти з anchor-посиланнями.
Базовий sticky header
Використовуємо position sticky з will-change оптимізацією. Додаємо тінь на порозі скролу. Приховуємо при скролі вниз, показуємо при скролі вгору на мобільних.
.site-header {
position: sticky;
top: 0;
z-index: 100;
background: #fff;
will-change: transform;
transition: box-shadow 0.2s ease;
}
.site-header--scrolled {
box-shadow: 0 2px 20px rgba(0, 0, 0, 0.1);
}
Hide-on-scroll: приховуємо при скролі вниз
Популярний паттерн: хедер зникає при скролі вниз та з'являється при скролі вгору. Корисно для економії місця на мобільних.
const THRESHOLD = 100
const HIDE_AFTER = 50
let scrollStart = 0
let hidden = false
function onScroll() {
const y = window.scrollY
const diff = y - lastScrollY
if (diff > 0) {
// Скролл вниз
if (!hidden && y > THRESHOLD && y - scrollStart > HIDE_AFTER) {
header.classList.add('site-header--hidden')
hidden = true
}
} else {
// Скролл вгору
if (hidden) {
header.classList.remove('site-header--hidden')
hidden = false
scrollStart = y
}
}
lastScrollY = y
}
React: хук для sticky-логіки
function useStickyHeader(hideOnScrollDown = true) {
const [state, setState] = useState({
isScrolled: false,
isVisible: true,
scrollY: 0,
})
useEffect(() => {
let ticking = false
const handler = () => {
if (ticking) return
ticking = true
requestAnimationFrame(() => {
const y = window.scrollY
setState(prev => ({
isScrolled: y > 10,
isVisible: hideOnScrollDown ? (y < 100 || prev.isVisible) : true,
scrollY: y,
}))
ticking = false
})
}
window.addEventListener('scroll', handler, { passive: true })
return () => window.removeEventListener('scroll', handler)
}, [hideOnScrollDown])
return state
}
Sticky footer / bottom navigation
Використовуємо fixed позиціонування для постійного футера. Застосовуємо safe-area-inset для iPhone safe zone.
.bottom-nav {
position: fixed;
bottom: 0;
padding-bottom: env(safe-area-inset-bottom);
}
Конфлікт з anchor-посиланнями
Використовуємо scroll-margin-top для врахування sticky header, що перекриває якорі:
[id] {
scroll-margin-top: calc(var(--header-height) + 16px);
}
Терміни
Sticky header з тінню при скролі — 1–2 години. З hide-on-scroll анімацією та React-хуком — половина дня. З bottom nav, safe-area, sticky sidebar — 1 день.







