creating floating shapes in CSS

Start with the HTML you want, you’ll need to create a container div with some elements. In my example, I’ll use some SVG squircles, courtesy of my co-worker George Francis who created squircley.app.

<ul>
  <li>
    <div class="item">
      <img src="location-to-your-file.svg" width="76px" height="76px" />
    </div>
  </li>
</ul>

We’ll apply a little bit of styling just to tidy things up…

ul {
  list-style: none;
  overflow: hidden;
  padding: 0;
  margin: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  padding: 16px 0;

  li {
    display: flex;
    width: 100%;
    position: relative;
    height: 64px;
  }

  .item {
    display: flex;
  }
}
  • firefox
  • slack
  • vscode

Now we have some shapes in a list 👏 But we’re not done yet.

You can create anything you like in these <li> tags - attach anything; a letter, a word, an image - it’s your world!

Next we’ll create the keyframes to animate the rotation and movement. A keyframe allows us to bundle up multiple changes in one animation. What we’re aiming for is the element to come from the left out of view, into view, and then off the screen on the right. I’ve also introduced a slow rotation to give it a ‘floating’ effect. Using transform with properties translate and rotate to achieve the movement.

@keyframes glide {
  from {
    left: 0;
    transform: translateX(-100%) rotate(0deg);
  }

  to {
    left: calc(100%);
    transform: translateX(100%) rotate(360deg);
  }
}

We’ll apply the new keyframe to the elements, with an infinite loop. Using a linear time scale to keep them floating at the same speed. We need to apply a little extra CSS to start the animation off-screen using position: absolute.

To apply the animation, you can guess we’ll need the animation property! (Who would’ve thought)

For performance reasons we’ll give the browser a hint that it’s going to be changing all the time using the property will-change.

ul {
  --animation-length: 15s;

  .item {
    display: flex;
    position: absolute;
    animation: glide var(--animation-length) infinite linear;
    transform: translateX(-100%);
    will-change: transform, left;
  }
}
  • firefox
  • slack
  • vscode

You’ll see all the elements start at the same time, so we want to delay each one increasing slowly for each element to make sure they slide in one by one.

Firstly, we’ll introduce a couple more custom variables. This is to make our lives easier when calculating how far each one should be apart to get an equal distance between each of them. You could automate this in JavaScript - but the aim is to keep this CSS only. Unfortunately, we can’t automate this using nth-child(n) and accessing the n value. That would be a real game-changer if we could.

ul {
  --animation-length: 15s;

  --number-of-items: 3;
  --animation-delay: calc(var(--animation-length) / var(--number-of-items));
}

For each child, we’ll increase the animation-delay. This property delays when the animation will kick in.

li:nth-child(1) > .item {
  animation-delay: calc(var(--animation-delay) * 0);
}

li:nth-child(2) > .item {
  animation-delay: calc(var(--animation-delay) * 1);
}

li:nth-child(3) > .item {
  animation-delay: calc(var(--animation-delay) * 2);
}
  • firefox
  • slack
  • vscode

That’s it! ✨ It’s simple to create this kind of animation - you can create all sorts using this trick, only imagination is your limit.