Note: In the snippet above, we are using the --space-smspacing variable defined in the CodyHouse framework (default value is 0.75em).

Since the .stack-cards__item element has a sticky position, as soon as the offset between it and the viewport is equal to --space-sm (top: var(--space-sm)), the element becomes fixed. By default, each card has a translateY value equal to the gap between cards. Therefore, even though the cards have the same top value, they're offset (offset = translateY).

We have also modified the transform-origin of the card element; we'll need this to create the stacking effect while scaling down the cards.

Let's use the Intersection Observer API to detect when the card elements enter the viewport and change their transform value based on the scrolling.

We can define a StackCards object that we use to initialize the stacking effect:

The effect will only work if the Intersection Observer API is supported ( intersectionObserverSupported === true) and if Reduces Motion is not enabled (we use the osHasReducedMotionutility function of the CodyHouse framework to check that).

The initStackCardsEffect function detects when the cards enter the viewport:

When the .js-stack-cards element is inside the viewport (entries[0].isIntersecting == true in stackCardsCallback() function), we listen to the window scroll event and update the transform value of each cards element accordingly (animateStackCards() function):