Feature blog image

Crafting Delightful Interactions

3 min read

Crafting Delightful Interactions: A Heart Button with Smooth Motion

In modern UI design, micro-interactions are crucial in enhancing the user experience. Well-crafted animations can transform simple actions into delightful interactions, making a product feel more intuitive and engaging.

In this article, I’ll break down how I implemented a Heart Button with satisfying motion feedback using Motion and Next.js.

Designing the Interaction

The goal was to create a like button that responds to user interactions with fluid animations and visual feedback, making each tap feel rewarding. Here’s what I focused on:

Smooth Scaling Animations — A slight scale-down effect on tap for a more tactile feel.

Fill Effect — A progressive heart fill animation to indicate engagement.

Particle Effects — Subtle bursts of particles when the button is fully activated.

Glow & Pulse Feedback — A soft glow effect when the like limit is reached.

Breaking Down the Code

1. Motion Variants for Animations

I used Framer Motion to define reusable animation variants:

const animations = {
  count: {
    initial: { y: -20, opacity: 0 },
    animate: { y: 0, opacity: 1 },
    exit: { y: 20, opacity: 0 },
  },
  heart: {
    initial: { scale: 1 },
    tapActive: { scale: 0.8 },
    tapCompleted: { scale: 1 },
  },
  pulse: {
    initial: { scale: 1.2, opacity: 0 },
    animate: { scale: [1.2, 1.8, 1.2], opacity: [0, 0.3, 0] },
    transition: { duration: 1.2 },
  },
};

Count Animation: The like count smoothly appears and disappears when updated. Heart Animation: The heart scales down when tapped, creating a snappy effect. Pulse Effect: A soft pulse animation when the like limit is reached.

2. Implementing the Heart Button

<Button variant="outline" onClick={handleClick} aria-pressed={isActive}>
  <motion.div
    animate={{ scale: isActive ? sizeMultiplier : 1 }}
    whileTap={
      isCompleted ? animations.heart.tapCompleted : animations.heart.tapActive
    }
    transition={{ type: "spring", stiffness: 300, damping: 15 }}
  >
    <Heart className="opacity-60" size={16} />
    <Heart
      className="absolute inset-0 text-red-500 fill-red-500"
      style={{ clipPath: `inset(${100 - fillPercentage}% 0 0 0)` }}
    />
  </motion.div>
  <span className="ml-1.5">Like</span>
  <motion.span
    key={count}
    variants={animations.count}
    transition={{ duration: 0.2 }}
  >
    {count}
  </motion.span>
</Button>

The button scales dynamically based on the number of likes. The filled heart grows progressively, visually indicating user engagement. The like count animates smoothly, making updates feel natural.

3. Adding Particle Effects

{
  isCompleted && (
    <motion.div className="absolute inset-0 pointer-events-none">
      {[...Array(6)].map((_, i) => (
        <motion.div
          key={i}
          className="absolute w-1 h-1 rounded-full bg-red-500"
          initial={animations.particle(i).initial}
          animate={animations.particle(i).animate}
          transition={animations.particle(i).transition}
        />
      ))}
    </motion.div>
  );
}

When the like limit is reached, tiny particles burst outward for a rewarding effect. Each particle follows a radial trajectory, creating a subtle but satisfying celebration.

Final Thoughts

Paying attention to these small details makes interactions feel smooth, responsive, and engaging. Well-crafted animations enhance the user experience, making even the simplest actions more enjoyable.

What are some of your favorite micro-interactions in UI design? Let’s discuss! 🚀

Category:
UI
Tags:
UI
UX
Nextjs
Motion
Microinteractions