<!-- @TODO: Move away from custom props before injecting the polyfill, as it can't handle it -->
<input type="checkbox" id="debug" />
<header>
<div>
<h1>Stacking Cards</h1>
<p>👇 Scroll down to see the effect.</p>
</div>
</header>
<main>
<ul id="cards">
<li class="card" id="card_1">
<div class="card__content">
<div>
<h2>Card One</h2>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p>
<p><a href="#top" class="btn btn--accent">Read more</a></p>
</div>
<figure>
<img src="https://codyhouse.co/demo-tutorials/stacking-cards/assets/img/img-1.jpg" alt="Image description">
</figure>
</div>
</li>
<li class="card" id="card_2">
<div class="card__content">
<div>
<h2>Card Two</h2>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p>
<p><a href="#top" class="btn btn--accent">Read more</a></p>
</div>
<figure>
<img src="https://codyhouse.co/demo-tutorials/stacking-cards/assets/img/img-2.jpg" alt="Image description">
</figure>
</div>
</li>
<li class="card" id="card_3">
<div class="card__content">
<div>
<h2>Card Three</h2>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p>
<p><a href="#top" class="btn btn--accent">Read more</a></p>
</div>
<figure>
<img src="https://codyhouse.co/demo-tutorials/stacking-cards/assets/img/img-3.jpg" alt="Image description">
</figure>
</div>
</li>
<li class="card" id="card_4">
<div class="card__content">
<div>
<h2>Card Four</h2>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p>
<p><a href="#top" class="btn btn--accent">Read more</a></p>
</div>
<figure>
<img src="https://codyhouse.co/demo-tutorials/stacking-cards/assets/img/img-2.jpg" alt="Image description">
</figure>
</div>
</li>
</ul>
</main>
<aside>
<p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Rem repellat non ratione eum provident quaerat obcaecati dicta autem voluptates sit cum quis distinctio, atque sint commodi reprehenderit perspiciatis iure velit molestiae eos molestias ipsa nihil quia? Porro tempore in fugit, aspernatur et temporibus aliquam magnam adipisci deleniti dicta repellat alias nostrum impedit quidem odit excepturi nam. Itaque numquam unde quae dignissimos exercitationem placeat, ipsa amet tempora vitae ullam animi! Hic asperiores in esse atque expedita dolorem officia illo commodi fugit earum ab, assumenda optio, sed repellat. Maiores laudantium reprehenderit suscipit accusamus assumenda, debitis nemo ut sapiente numquam doloremque alias explicabo minus autem harum illum dicta. Unde obcaecati inventore repellat id eligendi eaque, rerum aliquam maiores aspernatur cumque tenetur quo qui mollitia atque quasi voluptate ipsum neque explicabo voluptatem quibusdam beatae quae iste vero doloribus! Natus libero quibusdam molestias harum atque, assumenda doloribus quis, quam corporis, voluptas suscipit magni! Enim sapiente totam consequatur repellat maxime placeat perferendis iste maiores soluta, obcaecati incidunt expedita eius cumque ipsa quo sed. Minus distinctio culpa cumque labore nulla laborum ex, explicabo magni mollitia eaque aliquam vero blanditiis quae, id necessitatibus error voluptas harum tempora sint porro. Facilis beatae quasi soluta et ipsum suscipit repellendus inventore.</p>
<p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Possimus, ipsam molestias. Fuga similique, dolor tempora rerum perferendis ipsam ullam doloribus beatae esse facilis quae sit excepturi expedita non, sunt accusantium magnam? Animi nesciunt nostrum eligendi iusto voluptatum hic qui. Quod distinctio qui nobis et doloremque. Fuga, itaque? Laborum dignissimos nobis quaerat optio voluptatum similique unde, neque cupiditate, vel, fugiat dolore? Accusantium neque deserunt ducimus enim, illum laborum magni ipsam eos optio aspernatur nam necessitatibus quod natus, dolor laboriosam illo velit.</p>
<p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Rem repellat non ratione eum provident quaerat obcaecati dicta autem voluptates sit cum quis distinctio, atque sint commodi reprehenderit perspiciatis iure velit molestiae eos molestias ipsa nihil quia? Porro tempore in fugit, aspernatur et temporibus aliquam magnam adipisci deleniti dicta repellat alias nostrum impedit quidem odit excepturi nam. Itaque numquam unde quae dignissimos exercitationem placeat, ipsa amet tempora vitae ullam animi! Hic asperiores in esse atque expedita dolorem officia illo commodi fugit earum ab, assumenda optio, sed repellat. Maiores laudantium reprehenderit suscipit accusamus assumenda, debitis nemo ut sapiente numquam doloremque alias explicabo minus autem harum illum dicta. Unde obcaecati inventore repellat id eligendi eaque, rerum aliquam maiores aspernatur cumque tenetur quo qui mollitia atque quasi voluptate ipsum neque explicabo voluptatem quibusdam beatae quae iste vero doloribus! Natus libero quibusdam molestias harum atque, assumenda doloribus quis, quam corporis, voluptas suscipit magni! Enim sapiente totam consequatur repellat maxime placeat perferendis iste maiores soluta, obcaecati incidunt expedita eius cumque ipsa quo sed. Minus distinctio culpa cumque labore nulla laborum ex, explicabo magni mollitia eaque aliquam vero blanditiis quae, id necessitatibus error voluptas harum tempora sint porro. Facilis beatae quasi soluta et ipsum suscipit repellendus inventore.</p>
<p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Possimus, ipsam molestias. Fuga similique, dolor tempora rerum perferendis ipsam ullam doloribus beatae esse facilis quae sit excepturi expedita non, sunt accusantium magnam? Animi nesciunt nostrum eligendi iusto voluptatum hic qui. Quod distinctio qui nobis et doloremque. Fuga, itaque? Laborum dignissimos nobis quaerat optio voluptatum similique unde, neque cupiditate, vel, fugiat dolore? Accusantium neque deserunt ducimus enim, illum laborum magni ipsam eos optio aspernatur nam necessitatibus quod natus, dolor laboriosam illo velit.</p>
<p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Rem repellat non ratione eum provident quaerat obcaecati dicta autem voluptates sit cum quis distinctio, atque sint commodi reprehenderit perspiciatis iure velit molestiae eos molestias ipsa nihil quia? Porro tempore in fugit, aspernatur et temporibus aliquam magnam adipisci deleniti dicta repellat alias nostrum impedit quidem odit excepturi nam. Itaque numquam unde quae dignissimos exercitationem placeat, ipsa amet tempora vitae ullam animi! Hic asperiores in esse atque expedita dolorem officia illo commodi fugit earum ab, assumenda optio, sed repellat. Maiores laudantium reprehenderit suscipit accusamus assumenda, debitis nemo ut sapiente numquam doloremque alias explicabo minus autem harum illum dicta. Unde obcaecati inventore repellat id eligendi eaque, rerum aliquam maiores aspernatur cumque tenetur quo qui mollitia atque quasi voluptate ipsum neque explicabo voluptatem quibusdam beatae quae iste vero doloribus! Natus libero quibusdam molestias harum atque, assumenda doloribus quis, quam corporis, voluptas suscipit magni! Enim sapiente totam consequatur repellat maxime placeat perferendis iste maiores soluta, obcaecati incidunt expedita eius cumque ipsa quo sed. Minus distinctio culpa cumque labore nulla laborum ex, explicabo magni mollitia eaque aliquam vero blanditiis quae, id necessitatibus error voluptas harum tempora sint porro. Facilis beatae quasi soluta et ipsum suscipit repellendus inventore.</p>
<p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Possimus, ipsam molestias. Fuga similique, dolor tempora rerum perferendis ipsam ullam doloribus beatae esse facilis quae sit excepturi expedita non, sunt accusantium magnam? Animi nesciunt nostrum eligendi iusto voluptatum hic qui. Quod distinctio qui nobis et doloremque. Fuga, itaque? Laborum dignissimos nobis quaerat optio voluptatum similique unde, neque cupiditate, vel, fugiat dolore? Accusantium neque deserunt ducimus enim, illum laborum magni ipsam eos optio aspernatur nam necessitatibus quod natus, dolor laboriosam illo velit.</p>
</aside>
:root {
--card-height: 40vw;
--card-margin: 4vw;
--card-top-offset: 1em;
--numcards: 4;
--outline-width: 0px;
}
#cards {
padding-bottom: calc(var(--numcards) * var(--card-top-offset)); /* Make place at bottom, as items will slide to that position*/
margin-bottom: var(--card-margin); /* Don't include the --card-margin in padding, as that will affect the scroll-timeline*/
}
#card_1 {
--index: 1;
}
#card_2 {
--index: 2;
}
#card_3 {
--index: 3;
}
#card_4 {
--index: 4;
}
.card {
position: sticky;
top: 0;
padding-top: calc(var(--index) * var(--card-top-offset));
}
@supports (animation-timeline: works) {
@scroll-timeline cards-element-scrolls-in-body {
source: selector(body);
scroll-offsets:
/* Start when the start edge touches the top of the scrollport */
selector(#cards) start 1,
/* End when the start edge touches the start of the scrollport */
selector(#cards) start 0
;
start: selector(#cards) start 1; /* Start when the start edge touches the top of the scrollport */
end: selector(#cards) start 0; /* End when the start edge touches the start of the scrollport */
time-range: 4s;
}
.card {
--index0: calc(var(--index) - 1); /* 0-based index */
--reverse-index: calc(var(--numcards) - var(--index0)); /* reverse index */
--reverse-index0: calc(var(--reverse-index) - 1); /* 0-based reverse index */
}
.card__content {
transform-origin: 50% 0%;
will-change: transform;
--duration: calc(var(--reverse-index0) * 1s);
--delay: calc(var(--index0) * 1s);
animation: var(--duration) linear scale var(--delay) forwards;
animation-timeline: cards-element-scrolls-in-body;
}
@keyframes scale {
to {
transform:
scale(calc(
1.1
-
calc(0.1 * var(--reverse-index))
));
}
}
}
/** DEBUG **/
#debug {
position: fixed;
top: 1em;
left: 1em;
}
#debug::after {
content: " Show Debug";
margin-left: 1.5em;
color: white;
white-space: nowrap;
}
#debug:checked ~ main {
--outline-width: 1px;
}
/** PAGE STYLING **/
* { /* Poor Man's Reset */
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background: rgb(58 29 43);
color: rgb(255, 255, 255);
text-align: center;
font-size: calc(1em + 0.5vw);
}
header,
main {
width: 80vw;
margin: 0 auto;
}
header {
height: 100vh;
display: grid;
place-items: center;
}
#cards {
list-style: none;
outline: calc(var(--outline-width) * 10) solid blue;
display: grid;
grid-template-columns: 1fr;
grid-template-rows: repeat(var(--numcards), var(--card-height));
gap: var(--card-margin);
}
.card {
outline: var(--outline-width) solid hotpink;
}
.card__content {
box-shadow: 0 0.2em 1em rgba(0, 0, 0, 0.1), 0 1em 2em rgba(0, 0, 0, 0.1);
background: rgb(255, 255, 255);
color: rgb(10, 5, 7);
border-radius: 1em;
overflow: hidden;
display: grid;
grid-template-areas: "text img";
grid-template-columns: 1fr 1fr;
grid-template-rows: auto;
align-items: stretch;
outline: var(--outline-width) solid lime;
}
.card__content > div {
grid-area: text;
width: 80%;
place-self: center;
text-align: left;
display: grid;
gap: 1em;
place-items: start;
}
.card__content > figure {
grid-area: img;
overflow: hidden;
}
.card__content > figure > img {
width: 100%;
height: 100%;
object-fit: cover;
}
h1 {
font-weight: 300;
font-size: 3.5em;
}
h2 {
font-weight: 300;
font-size: 2.5em;
}
p {
font-family: sans-serif;
font-weight: 300;
line-height: 1.42;
}
.btn {
background: rgb(188 87 36);
color: rgb(255 255 255);
text-decoration: none;
display: inline-block;
padding: 0.5em;
border-radius: 0.25em;
}
aside {
width: 50vw;
margin: 0 auto;
text-align: left;
}
aside p {
margin-bottom: 1em;
}
import { showDialog } from 'https://codepen.io/bramus/pen/ZEqMOLz/cccfe67c2b9cdfbeb5fb59083dbd0a64.js';
showDialog('https://scroll-driven-animations.style/demos/stacking-cards/css/');
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.