0

Learning by Building: The Delightful Like Button Animation from Scratch

15 min read
...

When building my blog, I wanted to create a like button that was more than just functional – it needed to be delightful, memorable, and almost irresistible to click. This button is highly inspired by Josh Comeau's blog. I wanted to craft a similiar heart button with a liquid-filling animation that brings joy.

Here's a demo:

In this post, I'll break down how I built this heart button step by step. Best of all? I've integrated an interactive code playground so you can experiment with the code in real-time, tweak values, and see immediate results - because we all learn better by doing.

We'll start with simple SVG shapes and gradually add interactivity, animations, facial expressions, particle effects, and even sound feedback. Let's dive in!

The Vision: A Button Worth Clicking

Before diving into code, let's consider what makes an interactive UI element truly engaging:

  1. Visual feedback - The element should respond visually to user interaction
  2. Progressive changes - Multiple interactions should reveal more features or states
  3. Personality - Adding character through expressions or animations
  4. Multi-sensory feedback - Visual, auditory, and motion cues
  5. Surprising elements - Little delights that aren't immediately obvious

With these principles in mind, I designed a heart button that:

  • Fills up gradually when clicked, like liquid being poured in
  • Changes colors as it fills
  • Has a cute face that reacts to your interactions
  • Creates a brief celebration with particles
  • Plays a satisfying sound that increases in pitch

Now, let's build it together!

Quick tip: When you are working with the interactive code examples, you can collapse the TOC section - so you can have more space to interact with the playground.

Step 1: Starting with the Basic SVG Heart

First, let's create a simple heart shape. You can design it in Figma!

Experiment: Try changing the fill color to "#3b82f6" (blue) or "#10b981" (green), or try fill="none" to see just the outline.

Step 2: Creating a Stateful Button

Now, let's add interactivity by making the heart a clickable button with state:

Now our heart is a clickable button that tracks likes! The button increases the like count each time it's clicked, up to a maximum of 10 likes.

We've also calculated a fillPercentage based on the current number of likes compared to the maximum. We'll use this in the next step to create a visual fill effect.

Experiment: Try changing MAX_LIKES to different values (2, 5, 20) to see how it affects the button behavior.

Step 3: Adding the Fill Animation Using Clip Path

Let's make our heart visually respond to being clicked by adding a fill animation using the SVG clip-path property:

Now our heart fills up as we click! We achieved this with these key changes:

  1. Added a background heart with a light fill color
  2. Added a second heart path with a red fill color
  3. Applied a clip-path to the red heart that changes with the fillPercentage

The clip-path we're using creates a horizontal slice that moves upward as the fillPercentage increases. This gives the illusion of the heart filling up from bottom to top.

Experiment: Try modifying the clip-path to create different filling effects:

  • Fill from left to right:
    clipPath: `polygon(0% 0%, ${fillPercentage}% 0%, ${fillPercentage}% 100%, 0% 100%)`
    
  • Fill from center outward:
    clipPath: `circle(${fillPercentage/2}% at center)`
    

Step 4: Smooth Animation with useEffect

Our current implementation updates the fill instantly when clicking. Let's make the filling animation smoother by implementing a gradual fill using useEffect:

Now the heart fills up smoothly rather than instantly jumping to the new fill level! Here's how we accomplished this:

  1. Added a new state variable animatedFillPercentage to track the current animated fill level
  2. Used useEffect to create an animation loop when the target fillPercentage changes
  3. Used setInterval to gradually increment the animatedFillPercentage until it reaches the target
  4. Used useRef to keep track of the interval so we can clean it up properly

The key to smooth animation is making small incremental changes at a high frame rate. We increment by 2% every 16ms, which gives approximately 60 frames per second.

Experiment: Try changing the animation speed by modifying:

  • The increment value (replace currentPercentage += 2 with a smaller number for slower animation or larger for faster)
  • The interval timing (replace 16 with a different number - higher for slower, lower for faster)

Step 5: Adding Color Transitions

Let's make the heart change colors as it fills up:

Now our heart changes color as it fills up! We've added a getFillColor function that returns different colors based on the current animatedFillPercentage. This creates a rainbow effect as the heart fills, making the interaction more visually engaging.

Experiment: Try using HSL color interpolation for a smooth rainbow effect:

const getFillColor = (): string => {
  // Map percentage to hue (0-360)
  const hue = Math.floor((animatedFillPercentage / 100) * 360);
  return `hsl(${hue}, 80%, 65%)`;
};

Step 6: Adding Facial Expressions

Let's make our heart even more engaging by adding facial expressions that change based on the number of likes:

Now our heart has a face that changes expression based on how many likes it has received! We've added:

  1. A TypeScript type for different expressions
  2. A function to determine which expression to show based on like count
  3. Different SVG elements for eyes and mouth in each expression state

Each expression gets progressively happier as the number of likes increases:

  • neutral: Basic dots for eyes and a straight line for the mouth
  • smile: Same eyes but with a slight smile
  • happy: Same eyes with a bigger smile
  • very-happy: Curved eyebrows with a big smile
  • max: Upside-down eyebrows, biggest smile, and a blush mark

Experiment: Try creating a custom expression by modifying the SVG elements. For example, try to create a heart with a wink expression.

Step 7: Adding Particle Effects

Let's make our heart button even more engaging by adding particle effects when it's clicked:

Now when you click the heart button, it erupts with colorful particles! This adds a delightful sense of celebration to each interaction. Here's what we added:

  1. A Particle interface to define the properties of each particle
  2. A generateParticles function that creates randomized particles
  3. CSS animations to make the particles move and fade out
  4. A modified click handler that generates particles on each click

The particles have random:

  • Sizes
  • Colors (from our defined palette)
  • Directions (mostly upward)
  • Rotations

Experiment: Try modifying the particle behavior:

  • Change the number of particles (increase the number in the for loop)
  • Adjust the particle size range
  • Create different particle shapes by changing the borderRadius

Step 8: Using Framer Motion for Better Animations

The demos above use basic CSS and React state for animations. For production use, I recommend using a library like Framer Motion for smoother animations and better performance. Here's a simplified version that shows how we might implement some of these animations with Framer Motion:

Using Framer Motion gives us additional benefits:

  1. AnimatePresence - Properly handles the lifecycle of animated elements, including exit animations
  2. motion components - Makes it easy to add hover and tap animations to our button
  3. More fluid transitions - Framer Motion handles interpolation better than CSS alone
  4. Less code - We can define animations declaratively rather than imperatively

Notice how the particles now have a more fluid animation, and the button has hover and tap effects to make it feel more interactive.

The Complete Implementation

The heart button on this blog includes all the features we've covered, plus:

  1. Sound effects with increasing pitch
  2. 3D effects using shadows and highlights
  3. API integration to store like counts in a database
  4. Analytics tracking to monitor engagement
  5. Optimizations for performance on different devices

The complete source code for this button can be found here.

Closing thoughts

The web doesn't have to be boring. Small moments of delight like this heart button can transform boring interactions into memorable experiences. As developers, we have the power to create these moments of joy.

I hope this breakdown and the interactive playground - inspires you to experiment with your own ideas. If you build something delightful using these techniques, I'd love to see it! Share it with me on Twitter: @souravinsights.

Now go spread some joy on the web! ❤️