UI Elements

Receive Button

An interactive button component designed for payment or transaction interfaces with animated success states and tactile feedback.

receive-button.tsx
"use client";

import { type Variants, AnimatePresence, motion } from "motion/react";
import { Fingerprint, X } from "lucide-react";
import { useState } from "react";

// ease-out-quint for entering elements
const easeOutQuint: [number, number, number, number] = [0.23, 1, 0.32, 1];

const modalVariants: Variants = {
  hidden: { opacity: 0 },
  visible: {
    opacity: 1,
    transition: {
      staggerChildren: 0.06,
      delayChildren: 0.08,
    },
  },
  exit: {
    opacity: 0,
    transition: { duration: 0.15, ease: [0.25, 0.46, 0.45, 0.94] as [number, number, number, number] },
  },
};

const itemVariants: Variants = {
  hidden: { opacity: 0, y: -8, filter: "blur(4px)" },
  visible: {
    opacity: 1,
    y: 0,
    filter: "blur(0px)",
    transition: { duration: 0.35, ease: easeOutQuint },
  },
  exit: {
    opacity: 0,
    y: -4,
    transition: { duration: 0.12 },
  },
};

export default function ReceiveConfirmationModal() {
  const [isExpanded, setIsExpanded] = useState(false);

  const handleReceiveClick = () => setIsExpanded(true);
  const handleClose = () => setIsExpanded(false);
  const handleCancel = () => setIsExpanded(false);
  const handleConfirmReceive = () => {
    setIsExpanded(false);
  };

  return (
    <div className="relative flex justify-center items-center">
      <AnimatePresence mode="popLayout">
        {!isExpanded ? (
          <motion.button
            key="receive-button"
            layoutId="receive-button"
            onClick={handleReceiveClick}
            className="bg-white hover:bg-neutral-200 flex justify-center items-center cursor-pointer text-neutral-900 font-medium rounded-3xl text-lg w-[300px] h-11 shadow-lg transition-colors"
            initial={{ opacity: 0, scale: 0.95 }}
            animate={{ opacity: 1, scale: 1 }}
            exit={{ opacity: 0, scale: 0.95 }}
            transition={{ duration: 0.25, ease: easeOutQuint }}
            whileTap={{ scale: 0.97 }}
          >
            Receive
          </motion.button>
        ) : (
          <motion.div
            key="modal"
            className="relative bg-linear-to-b from-neutral-900 to-neutral-950 border border-neutral-800 rounded-3xl p-5 w-[430px] shadow-2xl backdrop-blur-xl flex flex-col gap-5"
            variants={modalVariants}
            initial="hidden"
            animate="visible"
            exit="exit"
            layout
          >
            {/* Header */}
            <motion.div
              className="flex flex-row items-center justify-between"
              variants={itemVariants}
            >
              <div className="flex items-center gap-3">
                <motion.div
                  initial={{ scale: 0.8, rotate: -10 }}
                  animate={{ scale: 1, rotate: 0 }}
                  transition={{ type: "spring", stiffness: 300, damping: 20, delay: 0.1 }}
                  className="text-neutral-300 bg-neutral-700/50 h-10 w-10 rounded-full p-2 flex justify-center items-center"
                >
                  <Fingerprint size={24} />
                </motion.div>
                <h2 className="text-neutral-100 text-lg font-medium tracking-wide">
                  Confirm
                </h2>
              </div>

              <motion.button
                onClick={handleClose}
                className="text-neutral-500 hover:text-neutral-300 transition-colors cursor-pointer"
                whileHover={{ scale: 1.1 }}
                whileTap={{ scale: 0.9 }}
                variants={itemVariants}
              >
                <X size={22} />
              </motion.button>
            </motion.div>

            {/* Message */}
            <motion.p
              className="text-neutral-400 text-start leading-relaxed"
              variants={itemVariants}
            >
              Are you sure you want to receive{" "}
              <span className="text-neutral-100 font-semibold">
                a hell load of money?
              </span>
            </motion.p>

            {/* Buttons */}
            <motion.div className="flex gap-4" variants={itemVariants}>
              <motion.button
                onClick={handleCancel}
                whileHover={{ scale: 1.02 }}
                whileTap={{ scale: 0.97 }}
                transition={{ type: "spring", stiffness: 400, damping: 17 }}
                className="flex-1 bg-neutral-800 hover:bg-neutral-700 cursor-pointer h-11 flex justify-center items-center text-neutral-300 font-medium rounded-full border border-neutral-700 transition-colors"
              >
                Cancel
              </motion.button>

              <motion.button
                layoutId="receive-button"
                onClick={handleConfirmReceive}
                whileHover={{ scale: 1.03 }}
                whileTap={{ scale: 0.97 }}
                transition={{ type: "spring", stiffness: 400, damping: 17 }}
                className="flex-1 bg-white hover:bg-neutral-200 h-11 flex justify-center items-center cursor-pointer text-neutral-900 font-semibold rounded-full shadow-lg transition-colors"
              >
                Receive
              </motion.button>
            </motion.div>
          </motion.div>
        )}
      </AnimatePresence>
    </div>
  );
}