Сегодня покажу, как выкинуть JS из связки «скролл + эффекты». Речь про CSS scroll-driven animations — это уже не polyfill, а нативный API в Chrome/Edge/Safari 26+. Два главных героя: scroll() и view(). Поехали.
**Основы**
scroll() привязывает анимацию к прогрессу прокрутки контейнера. Элемент просто крутится/двигается/закрашивается в зависимости от того, сколько пикселей мы проскроллили.
view() привязывает анимацию к появлению элемента во вьюпорте. Элемент «оживает», когда вползает в кадр, и «умирает», когда выходит. И это идеально для параллакса или плавного появления карточек.
Оба механизма работают через animation-timeline, а за точками старта и конца управляет animation-range.
**Прогресс-бар без единой строчки JS**
Классика: полоска сверху показывает, сколько прочитано. Раньше — слушатель скролла и requestAnimationFrame. Теперь — 5 строк CSS:
.progress { position: fixed; top: 0; left: 0; width: 100%; height: 4px; background: #ff4d00; transform-origin: left; animation: scale linear; animation-timeline: scroll(); }
@keyframes scale { from { transform: scaleX(0); } to { transform: scaleX(1); } }
Всё. scroll() без аргументов = отслеживаем скролл ближайшего предка (или корня, если предка-скроллера нет). Анимация идёт ровно от верха страницы до низа.
**Параллакс через view()**
Берём картинку, которая должна двигаться с другой скоростью, пока она на экране. Вместо scroll-слушателя и пересчёта позиций:
.parallax-image { animation: drift linear; animation-timeline: view(); animation-range: entry 0% cover 40%; }
@keyframes drift { from { transform: translateY(15%); } to { transform: translateY(-15%); } }
Поясню значения entry, cover, exit. Палитра простая: - entry — элемент выезжает снизу/сверху в кадр - cover — полностью на экране - exit — покидает вьюпорт
entry 0% cover 40% значит: анимация начинается в момент появления и заканчивается, когда элемент прошёл 40% высоты экрана. За счёт этого картинка «сдвигается» независимо от скорости скролла.
**Важный нюанс: animation-range**
Если у вас контейнер высотой 200%, а анимация должна идти только на середине скролла — играйтесь именно через range. scroll(root) + range: 0% 50% даст эффект, заканчивающийся на половине страницы. Это заменяет кучу логики «если y > X».
**Бонус-фича: animation-timeline: view(block)**
По умолчанию отслеживается ось блока (вертикаль) или инлайна (горизонталь). Можно явно указать view(inline) для горизонтальных слайдеров-каруселей с прокруткой по X. Тоже без JS.
**Что под капотом и поддержка**
Работает только с ключевыми кадрами — CSS-переходы так не умеют. animation-timeline можно комбинировать с обычной animation-duration, но при наличии timeline длительность игнорируется: прогрессом правит скролл.
Поддержка в 2025-м: Chrome 115+, Edge 115+, Safari 26+ (с префиксом -webkit- в iOS 26). Firefox — за флагом, но уже в активной разработке. Для отстающих браузеров это деградирует безболезненно: без поддержки просто не будет анимации.
**Моя рекомендация**
Не пишите свой параллакс через JS, если это обычный маркетинговый лендинг. Нативный scroll-driven API считается на стороне compositor, поэтому дёрганий и микролагов на слабых устройствах меньше, чем при ручном обновлении свойств из JS.
Вывод: добавьте scroll() на прогресс-бар, view() на карточки — почувствуете разницу в скорости вёрстки сразу.