Inspired by an animation I saw this morning, I decided to try creating something along those lines — though not nearly as impressive — using JavaScript 🎨. It seemed like a fun and challenging task.
So, let's dive in!
We'll start with a simple structure:
<main>
<input type="range" min="0" max="100" value="0" />
<div class="container"></div> <!-- this is where we'll place the hearts -->
</main>
The CSS plays a significant role in this project, and I'd like to highlight two key aspects:
- Accent Color
- CSS Animations
I couldn't remember how to change the default look of the <input type="range" />
element. During my research, I came across various vendor-prefixed pseudo-elements like ::-webkit-slider-runnable-track and ::-moz-range-track but the support for these wasn't great.
Then I came across a handy little CSS property called accent-color. I had seen it a couple of times before, but not often enough for it to stick in my memory. It's also a perfect fit for styling checkbox
and radio
input elements.
Good job, CSS Working Group! 🥳
input {
/* ... */
accent-color: white;
}
I usually enjoy coding CSS animations from scratch, fine-tuning them to align with the design goals or specific creative direction. But today, I wasn't feeling it, so I asked ChatGPT to write three organic floating animations with some variations and randomness. It didn't nail it on the first try, so instead of tweaking the animations by hand, I modified the prompt. While it might have been faster to code manually, after some time, the result was good enough.
edit: I fine-tuned the animations 😁
The one thing I was particularly picky about was the syntax. Initially, it generated something like this:
0% {
transform: translateX(0px) translateY(0px) rotateX(45deg) rotateY(30deg) scale(0.25);
}
My reaction was like Michael Scott’s famous "Oh God, Please No!" meme. The CSS Transform Module Level 2 syntax looks so much cleaner:
0% {
translate: 0px 0px;
rotate: 45deg 30deg;
scale: 0.25;
}
But I'm still getting used to this 'new' syntax too, so no big problems here.
Okay, let's fire up the engine 🚛.
I found it challenging to explain my thought process, but I spent nearly an hour just thinking through the problem. Here’s a breakdown of my approach:
1. Listen to the input event.
2. Create a new element when the event fires.
3. Animate each element up from where the input is located.
4. Remove each element after it finishes animating.
However, there’s no straightforward way to determine where the "slider's thumb" is on the screen, just the input element itself. Turns out there was a simple solution to this:
range.addEventListener('input', (e) => {
// ...
const heart = document.createElement('div');
heart.style.left = `${e.target.value}%`;
// ...
}
Feel free to check out the code.
I could refine the code for readability, structure it better with functional programming, or even enhance the animations further, but it serves its purpose well for a quick experiment.
I hope you enjoy it, and happy coding!
A big thanks to Chris Gannon for his inspiring work.