How to Create an Animated Floating Hearts Effect (Pure CSS & HTML)
Source: Dev.to

1. The HTML Structure
The markup is straightforward: a wrapper that acts as the “sky” and a set of <div class="heart"> elements that become the hearts.
The wrapper must have position: relative; and overflow: hidden; so the hearts stay contained and disappear smoothly when they exit the top boundary.
<div class="sky">
<div class="heart x1"></div>
<div class="heart x2"></div>
<div class="heart x3"></div>
<!-- add more hearts as needed -->
</div>
<h2>Our customers love us!</h2>
2. Drawing a Heart with Pure CSS
A heart is created with a single element and its ::before and ::after pseudo‑elements.
The technique builds a square and adds two overlapping pill‑shaped circles.
.heart {
position: relative;
}
/* Top halves of the heart */
.heart::before,
.heart::after {
position: absolute;
content: "";
left: 18px;
top: 0;
width: 18px;
height: 30px;
background: #CC2022; /* Heart colour */
border-radius: 30px 30px 0 0; /* Pill shape */
transform: rotate(-45deg);
transform-origin: 0 100%;
}
.heart::after {
left: 0;
transform: rotate(45deg);
transform-origin: 100% 100%;
}
3. Creating the Movement (Keyframes)
Hearts need to float upward and sway side‑to‑side simultaneously. Two keyframe animations handle this:
/* Vertical movement */
@keyframes moveclouds {
0% { top: 500px; }
100% { top: -500px; }
}
/* Horizontal sway */
@keyframes sideWays {
0% { margin-left: 0px; }
100% { margin-left: 50px; }
}
4. Adding Variety
If every heart behaved identically, the effect would look robotic. By assigning different classes (.x1 … .x16) we vary size, opacity, horizontal start position, and animation speed.
/* Small, semi‑transparent, fast */
.x1 {
left: 5%;
transform: scale(0.9);
opacity: 0.6;
animation:
moveclouds 15s linear infinite,
sideWays 4s ease-in-out infinite alternate;
}
/* Larger, more opaque, slower */
.x2 {
left: 25%;
transform: scale(0.6);
opacity: 0.9;
animation:
moveclouds 25s linear infinite,
sideWays 5s ease-in-out infinite alternate;
}
/* Very fast heart on the far right */
.x5 {
left: 88%;
transform: scale(0.8);
opacity: 0.3;
animation:
moveclouds 7s linear infinite,
sideWays 1s ease-in-out infinite alternate;
}
What these properties do
left– Distributes hearts horizontally across the container.transform: scale()– Adjusts size to create depth (parallax effect).opacity– Controls perceived distance by fading distant hearts.animation– Runs both the vertical and horizontal animations with class‑specific durations, giving an organic, randomized look.
Conclusion
By grouping the hearts inside a container with overflow: hidden, you get a lightweight, visually appealing floating‑hearts effect that doesn’t hurt performance. The code can be adapted to any framework (e.g., Alpine.js) and customized by changing background colours, animation speeds, or the number of heart instances.
Happy coding!