This demo has several versions:
scroll-timeline
ScrollTimeline
In this demo the cards at the top stack onto each other. When a card is stuck, it scales down so that the following card stacks on top of it.
Original demo by CodeHouse: https://codyhouse.co/tutorials/how-stacking-cards
.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 */ } @keyframes scale { to { transform: scale(calc(1.1 - calc(0.1 * var(--reverse-index)))); } } #cards { --numcards: 4; view-timeline-name: --cards-element-scrolls-in-body; } .card__content { --start-range: calc(var(--index0) / var(--numcards) * 100%); --end-range: calc((var(--index)) / var(--numcards) * 100%); animation: linear scale forwards; animation-timeline: --cards-element-scrolls-in-body; animation-range: exit-crossing var(--start-range) exit-crossing var(--end-range); }
To keep the cards stuck, position: sticky is used. Key for this part, is that this stickiness is not applied on the cards themselves (.card) but on the inner .card__content.
position: sticky
.card
.card__content
As for the animation, the key part is that the wrapping element #cards is being tracked on the entry-crossing range.
#cards
entry-crossing
The content of each card (.card__content) are animated and are assigned to a part of that range. As this demo contains 4 cards, each card should animate over 25% of the range. This is calculated using the --numcards and --index custom properties.
4
25%
--numcards
--index
By animating the .card__content elements and not the .card elements, the height of the #cards wrapper â and thus the available scroll estate â is not affected.
â ī¸ Your browser does not support Scroll-driven Animations. Please use Chrome 115 or newer.
đ Scroll down to see the effect.
Lorem ipsum dolor sit amet consectetur adipisicing elit.
Read more