Staggered fade-up
Tiles rise into place in a short wave as each row enters the viewport.
Cards grow from small and faded to full size as they cross the viewport. No JavaScript.
animation-timeline: view() links a normal keyframe animation to the element’s own trip through the viewport. animation-range: entry 0% cover 40% means the animation runs from the moment the card peeks in until it is 40% of the way across.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Zoom into view</title>
<style>
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; }
body { font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; -webkit-font-smoothing: antialiased; }
body { background: #e8f0ff; color: #12204a; }
.head { height: 60vh; display: grid; place-content: center; text-align: center; }
.head h1 { font-size: 3.2rem; margin: 0; }
.head p { font-family: ui-monospace, monospace; }
.cards { display: grid; gap: 28px; padding: 0 10vw 40vh; }
.card {
height: 40vh; border-radius: 20px; display: grid; place-items: center;
font-size: 5rem; font-weight: 800; color: #fff;
background: linear-gradient(135deg, #3355ff, #8a5cff);
}
.card:nth-child(even) { background: linear-gradient(135deg, #ff5c8a, #ffb35c); }
@supports (animation-timeline: view()) {
.card {
animation: zoom-in linear both;
animation-timeline: view();
animation-range: entry 0% cover 40%;
}
}
@keyframes zoom-in {
from { opacity: 0; transform: scale(.6) translateY(40px); }
to { opacity: 1; transform: none; }
}
</style>
</head>
<body>
<header class="head"><h1>Pure CSS</h1><p>animation-timeline: view()</p></header>
<main class="cards">
<article class="card">01</article><article class="card">02</article>
<article class="card">03</article><article class="card">04</article>
<article class="card">05</article><article class="card">06</article>
<article class="card">07</article><article class="card">08</article>
</main>
</body>
</html>