IntersectionObserver или scroll-listener:1. reading progress bar
2. sticky-reveal секции
Главная идея: прогресс анимации привязан не ко времени, а к скроллу.
animation-timeline: scroll(root block);
или к появлению конкретного блока во viewport:
animation-timeline: --section;
animation-range: entry 15% cover 45%;
То есть браузер сам считает progress timeline, а мы описываем только CSS-анимацию.
Пример: progress bar + sticky-reveal без JS
<div class="read-progress"></div>
<section class="feature">
<div class="feature__sticky">
<h2>Scroll-driven reveal</h2>
<p>Блок раскрывается по мере прохождения секции.</p>
</div>
</section>
.read-progress {
position: fixed;
z-index: 1000;
inset: 0 0 auto;
block-size: 3px;
background: #7c3aed;
transform-origin: 0 50%;
transform: scaleX(0);
display: none;
}
.feature { min-block-size: 180vh; }
.feature__sticky {
position: sticky;
top: 20svh;
opacity: 1;
transform: none;
}
@supports (animation-timeline: scroll()) {
.read-progress {
display: block;
animation: progress linear both;
animation-timeline: scroll(root block);
}
@keyframes progress {
to { transform: scaleX(1); }
}
.feature {
view-timeline-name: --feature;
view-timeline-axis: block;
}
.feature__sticky {
opacity: 0;
transform: translateY(24px) scale(.98);
animation: stickyReveal linear both;
animation-timeline: --feature;
animation-range: entry 15% cover 45%;
will-change: opacity, transform;
}
@keyframes stickyReveal {
to {
opacity: 1;
transform: none;
}
}
@media (prefers-reduced-motion: reduce) {
.read-progress,
.feature__sticky {
animation: none;
}
.feature__sticky {
opacity: 1;
transform: none;
}
}
}Что важно для продакшена:
• Заворачивать в
@supports Scroll-Driven Animations пока не стоит считать безусловным baseline. Делайте progressive enhancement: без поддержки пользователь просто видит обычный контент.
• Не прятать контент по умолчанию
Плохой паттерн:
.card {
opacity: 0;
}А потом раскрывать только внутри scroll-animation. В браузере без поддержки блок останется невидимым. Лучше базово показывать контент, а стартовое состояние задавать внутри
@supports.• Анимировать дешёвые свойства
transform и opacity — ок. height, top, margin, layout-зависимые штуки — осторожно. Scroll-driven не делает дорогую анимацию бесплатной.•
IntersectionObserver всё ещё нужен не всегда, но и не умер Для визуальных reveal/progress эффектов CSS часто проще и чище. Но если нужно грузить данные, отправлять аналитику, управлять состоянием приложения или синхронизироваться с бизнес-логикой — это всё ещё зона JS.
• Для sticky-reveal лучше таймлайн секции, а не самого sticky-элемента
Sticky-элемент может визуально «залипать», поэтому удобнее повесить
view-timeline-name на родительскую секцию, а анимировать внутренний sticky-блок через named timeline.Итог: для progress bars и декоративных sticky-reveal эффектов Scroll-Driven Animations позволяют убрать лишний JS, не плодить observers/listeners и оставить поведение декларативным. Главное — progressive enhancement,
prefers-reduced-motion и нормальный fallback.
