Параллакс-эффекты теперь можно делать на чистом CSS. Раньше для этого требовался JavaScript: слушатель событий скролла, пересчёт позиций в каждом кадре, ручное дёрганье элементов вверх-вниз. Всё это заменяют CSS Scroll-driven animation timelines.
Ключевой трюк — свойство view-timeline-name. Оно создаёт view progress timeline: шкала прогресса показывает, как далеко элемент .parallax прошёл через область видимости. 0% — элемент только начинает входить во вьюпорт, 100% — полностью покинул его. view-timeline-axis: block заставляет трекер отслеживать движение по вертикальной оси (block axis для обычного режима письма).
У дочернего элемента animation-timeline: --parallax-tl меняет источник времени для анимации — теперь это не секунды, а прогресс скролла. Длительность выставляется на auto, easing — linear, режим заполнения — both, чтобы ключевые кадры держались за пределами активного диапазона. Важный нюанс: свойство animation-timeline не входит в shorthand animation, его нужно прописывать отдельно, и обязательно после shorthand, иначе он сбросит все неупомянутые longhand-свойства.
Сами keyframes двигают translate от 0 -20% до 0 20% при скролле (значение по умолчанию для переменной --parallax-offset — 20). Элемент движется с другой скоростью, чем контейнер, — получается иллюзия глубины.
Но есть проблема: если дочерний элемент точно такого же размера, как контейнер, сдвиг оголит пустое пространство сверху или снизу. Решение — масштабирование. В CSS ребёнок масштабируется по формуле scale: calc(1 + var(--parallax-offset, 20) * 2 / 100). При offset = 20 это 140% от исходного размера. Излишки обрезаются через overflow: hidden на контейнере. Красота в том, что и translate, и scale читают одну и ту же переменную --parallax-offset: крутишь один параметр — масштаб сам подстраивается, пустые места не появляются.
Последний штрих — will-change: translate. Это подсказка браузеру, что свойство translate будет меняться, и он может заранее вынести элемент на отдельный слой для производительности.
Параллакс — это движение, привязанное к скроллу. Не все хотят его видеть. Хороший тон — отключать эффект для тех, у кого в системе включено prefers-reduced-motion: reduce. Достаточно сбросить анимацию в none и scale в 1 для .parallax > * внутри соответствующего медиа-запроса.