Apple Music has this “Spatial Audio” feature where the direction of the music in your headphones is based on the location of the device. It’s tough to explain just how neat it is. But that’s not what I’m here to talk about.
I opened up the Apple Music app and saw a featured playlist of hit songs that support Spatial Audio. The cover for it is this brightly-colored pink container that holds a bunch of boxes stacked one on top of another. The boxes animate in one at a time, fading in at the center of the container, then fading out as it scales to the size of the container. Like an infinite loop.
Cool! I knew I had to re-create it in CSS. So I did.
Here’s how it works…
The markup
Table of Contents
I started with the HTML. There’s obviously a container we need to define, plus however many boxes we want to animate. I went with an even 10 boxes in the container.
<div class="container"> <div class="box"></div> <div class="box"></div> <div class="box"></div> <!-- etc. -->
</div>
That’s literally it for HTML. We are free to jump right into the CSS!
Styling the container
Nothing too fancy here. I measured approximate dimensions based on what I saw in Apple Music, which happened to be 315px
× 385px
. then I took a screenshot of the cover and dropped it into my image editing app to get the lightest possible color, which is around the outside edges of the container. My color picker landed on #eb5bec
.
.container { background-color: #eb5bec; height: 315px; width: 385px;
}
As I was doing this, I knew I would probably want this to be a grid container to align the boxes and any other elements in the center. I also figured that the boxes themselves would start from the center of the container and stack on top of one another, meaning there will be some absolute positioning. That also means the container ought to have relative positioning to reign them in.
.container { background-color: #eb5bec; height: 315px; position: relative; width: 385px;
}
And since we want the boxes to start from the center, we can reach for grid to help with that:
.container { background-color: #eb5bec; display: grid; height: 315px; place-items: center; position: relative; width: 385px;
}
If the boxes in the container are growing outward, then there’s a chance that they could expand beyond the container. Better hide any possible overflow.
.container { background-color: #eb5bec; height: 315px; overflow: hidden; position: relative; width: 385px;
}
I also noticed some rounded corners on it, so let’s drop that in while we’re here.
.container { background-color: #eb5bec; border-radius: 16px; height: 315px; position: relative; width: 385px;
}
So far, so good!
Styling the boxes
We have 10 .box
elements in the markup and we want them stacked on top of one another. I started with some absolute positioning, then sized them at 100px
square. Then I did the same thing with my image editing app to find the darkest color value of a box, which was #471e45
.
.box { background: #471e45; height: 100px; position: absolute; width: 100px;
}
The boxes seem to fade out as they grow. That allows one box to be seen through the other, so let’s make them opaque to start.
.box { background: #471e45; height: 100px; opacity: 0.5; position: absolute; width: 100px;
}
Cool, cool. We’re unable to see all the boxes as they’re stacked on top of one another, but we’re making progress!
Creating the animation
Ready to write some @keyframe
s? We’re gonna make this super simple, going from 0 to 100% without any steps in between. We don’t even need those percentages!
@keyframes grow { from { /* do stuff */ } to { /* do stuff */ }
}
Specifically, we want two things to happen from start to finish:
- The boxes go from our starting opacity value of
0.5
to0
(fully transparent). - The boxes scale up to the edges of the container.
@keyframes grow { from { opacity: 0.5; transform: scale(0); } to { opacity: 0; transform: scale(3.85); }
}
How’d I land on scaling the boxes up by 3.85
? Our boxes are 100px
square and the container is 385px
tall. A value of 3.85
gets the boxes up to 385px
as they fade completely out which makes for a nice linear animation when we get there.
Speaking of which…
Applying the animation
It’s pretty easy to call the animation on our boxes. Just gotta make sure it moves in a liner timing function on an infinite basis so it’s like the Energizer Bunny and keeps going and going and going and going and…
.box { animation: grow 10s linear infinite; /* 10s = 10 boxes */ /* etc. */
}
This gives us the animation we want. But! The boxes are all moving at the same time, so all we see is one giant box growing.
We’ve gotta stagger those little fellers. No loops in vanilla CSS, unfortunately, so we have to delay each box individually. We can start by setting a custom property for the delay, set it to one second, then redefine the custom property on each instance.
.box { --delay: 1s; animation-delay: var(--delay); /* same as before */
}
.box:nth-child(2) { --delay: 2s;
}
.box:nth-child(3) { --delay: 3s;
}
.box:nth-child(4) { --delay: 4s;
}
.box:nth-child(5) { --delay: 5s;
}
/* five more times... */
Huzzah!
Keep on rockin’
That’s it! We just recreated the same sort of effect used by Apple Music. There are a few finishing touches we could plop in there, like the content and whatnot. Here’s my final version again: