v146 · CSS · Basic Trigger Demo
Basic Trigger Demo
Scroll down to see cards animate in. Chrome 146 uses animation-trigger: view() to fire the animation the moment an element enters the scrollport — purely in CSS. Older browsers get an IntersectionObserver fallback that adds an .in-view class.
animation-trigger. On older browsers this demo uses a JavaScript IntersectionObserver fallback so the animations still work — scroll down to test.
single cards
Card 1 — Fade and slide up
This card fades in and slides up when it enters the viewport. The animation plays once and stays at its end state.
Card 2 — Same animation
Each card has the same animation-trigger: view() declaration. The browser fires each animation independently as the element scrolls into view.
Card 3 — No JavaScript required
In Chrome 146, this is pure CSS — no IntersectionObserver, no class toggling. The browser handles the scroll detection and animation scheduling on the compositor thread.
staggered grid
Six cards in a 2-column grid. Odd columns trigger immediately, even columns get a 100 ms delay — creating a gentle stagger with no JavaScript coordination needed.
Grid A
First column, no delay.
Grid B
Second column, 100 ms delay.
Grid C
First column, no delay.
Grid D
Second column, 100 ms delay.
Grid E
First column, no delay.
Grid F
Second column, 100 ms delay.
CSS (native + fallback)
/* The animation */
@keyframes fade-slide-up {
from { opacity: 0; translate: 0 2.5rem; }
to { opacity: 1; translate: 0 0; }
}
/* Fallback: start hidden, JS adds .in-view class */
.card {
opacity: 0;
translate: 0 2.5rem;
transition: opacity 0.5s ease, translate 0.5s ease;
}
.card.in-view {
opacity: 1;
translate: 0 0;
}
/* Native scroll trigger — Chrome 146+ */
@supports (animation-trigger: view()) {
.card {
opacity: 1; /* reset — animation handles it */
translate: 0 0;
transition: none;
animation: fade-slide-up 0.55s ease both;
animation-trigger: view(block 10% 90%);
}
}
/* Stagger via animation-delay */
.grid .card:nth-child(even) { animation-delay: 0.1s; }
JavaScript fallback
// Only runs when animation-trigger is not supported
if (!CSS.supports('animation-trigger', 'view()')) {
const observer = new IntersectionObserver(entries => {
for (const entry of entries) {
if (entry.isIntersecting) {
entry.target.classList.add('in-view');
observer.unobserve(entry.target); // once only
}
}
}, { threshold: 0.1 });
document.querySelectorAll('.card').forEach(el => observer.observe(el));
}
see also
- Scroll Position Demo — progress bars tied to scroll position
- ChromeStatus entry
- MDN — animation-trigger