A worked example, rendered from real sample data. Sign in to run the tool on your own input.
fade-up/* IntersectionObserver — works everywhere */
.reveal {
opacity: 0; transform: translateY(40px);
transition: transform 700ms cubic-bezier(0.22, 1, 0.36, 1), opacity 700ms ease, filter 700ms ease;
will-change: transform, opacity, filter;
}
.reveal.in {
opacity: 1; transform: translateY(0);
}
const items = document.querySelectorAll(".reveal");
items.forEach((el, i) => { el.style.transitionDelay = (i * 120) + "ms"; });
const io = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add("in");
io.unobserve(entry.target);
}
});
}, { threshold: 0.15 });
items.forEach((el) => io.observe(el));
/* Scroll-driven CSS — no JavaScript, Chrome 115+ / Edge 115+ / Safari 26+ */
@supports (animation-timeline: view()) {
.reveal {
animation: reveal-in 700ms cubic-bezier(0.22, 1, 0.36, 1) both;
animation-timeline: view();
animation-range: entry 20% cover 60%;
}
@keyframes reveal-in {
from { opacity: 0; transform: translateY(40px); }
to { opacity: 1; transform: translateY(0); }
}
}
@media (prefers-reduced-motion: reduce) {
.reveal {
transition: none;
animation: none;
opacity: 1; transform: translateY(0);
}
}
/* HTML */
<div class="reveal">Section 1</div>
<div class="reveal">Section 2</div>
<div class="reveal">Section 3</div>
Generate CSS scroll-triggered animation code with IntersectionObserver. Part of the DevTools Surf developer suite. Browse more tools in the Animation / CSS collection.