Stacked Cards with Sticky Positioning and a Dash of Sass

Avatar of Robin Rendle
Robin Rendle on

The other day, I spotted this particularly lovely bit from Corey Ginnivan’s website where a collection of cards stack on top of one another as you scroll.

I started wondering how much JavaScript this would involve and how you’d go about making it when I realized — ah! — this must be the work of position: sticky and a tiny amount of Sass. So, without diving into how Corey did this, I decided to take a crack at it myself.

First up, some default styles for the cards:

body {
  background: linear-gradient(#e8e8e8, #e0e0e0);
}

.wrapper {
  margin: 0 auto;
  max-width: 700px;
}

.card {
  background-color: #fff;
  border: 1px solid #ccc;
  border-radius: 10px;
  box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.1);
  color: #333;
  padding: 40px;
}

Next, we need to make each card sticky to the top of the wrapper. We can do that like this:

.card {
  position: sticky;
  top: 10px;
  // other card styles
}

And that leaves us with this:

But how do we get each of these elements to look like a stack on top of one another? Well, we can use some fancy Sass magic to fix the position of each card. First we’ll loop over every card element and then change the value with each iteration:

@for $i from 1 through 8 {
  .card:nth-child(#{$i}n) {
    top: $i * 20px;
  }
}

Which results in this demo, which is totally charming, if I do say so myself:

And there we have it! We could make a few visual changes here to improve things. For example, the box-shadow and color of each card, just like Corey’s example. But I wanted to keep experimenting here. What if we switch the order of the cards and made them horizontal instead?

We already do that on this very website:

After experimenting for a little bit I changed the order of the cards with flexbox and made each item slide in from right to left:

.wrapper {
  display: flex;
  overflow-x: scroll;
}

.card {
  height: 60vh;
  min-width: 50vw;
  position: sticky;
  top: 5vh;
  left: 10vw;
}

But I also wanted to make each of the cards come in at different angles so I updated the Sass loop with the random function:

@for $i from 1 through 8 {
  .card:nth-child(#{$i}n) {
    left: $i * 20px;
    left: random(200) + $i * 1px;
    top: random(130) + $i * 1px;
    transform: rotate(random(3) - 2 * 1deg);
  }
}

That’s the bulk of the changes and that results in the following:

Pretty neat, eh? I love position: sticky; so much.