Text Fall Button
Button characters fall out downward on hover while a new set enters from above — two staggered layers.
Use and inspectUse this when the motion supports the surrounding content. Connect callbacks and state to your app, keep the reset preview as a local demonstration, and add a reduced-motion fallback for movement or looping. The preview is synthetic; it does not call a backend.Next: inspect the source below and replace the demo inputs with your own data.
Text Fall ButtonButton characters fall out downward on hover while a new set enters from above — two staggered layers.
"use client";
import { useState } from "react";
import { motion } from "framer-motion";
type TextFallButtonProps = {
text: string;
onClick?: () => void;
};
export function TextFallButton({ text, onClick }: TextFallButtonProps) {
const [hovered, setHovered] = useState(false);
const chars = text.split("");
return (
<button
className="mp-text-fall-btn"
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
onClick={onClick}
>
<span className="mp-text-fall-clip" aria-hidden>
{/* Primary layer — exits downward on hover */}
<span className="mp-text-fall-layer">
{chars.map((char, i) => (
<motion.span
key={i}
animate={
hovered
? { y: "110%", opacity: 0 }
: { y: "0%", opacity: 1 }
}
transition={{
duration: 0.22,
delay: hovered ? i * 0.025 : (chars.length - 1 - i) * 0.025,
ease: hovered ? "easeIn" : "easeOut",
}}
style={{ display: "inline-block" }}
>
{char === " " ? "\u00A0" : char}
</motion.span>
))}
</span>
{/* Secondary layer — enters from above on hover */}
<span className="mp-text-fall-layer mp-text-fall-layer--over" aria-hidden>
{chars.map((char, i) => (
<motion.span
key={i}
animate={
hovered
? { y: "0%", opacity: 1 }
: { y: "-110%", opacity: 0 }
}
transition={{
duration: 0.22,
delay: hovered ? i * 0.025 : 0,
ease: hovered ? "easeOut" : "easeIn",
}}
style={{ display: "inline-block" }}
>
{char === " " ? "\u00A0" : char}
</motion.span>
))}
</span>
</span>
{/* Accessible text */}
<span className="mp-text-fall-sr">{text}</span>
</button>
);
}Install
npm install framer-motionProps
| Prop | Type | Default | Description |
|---|---|---|---|
| textrequired | string | — | Button text |
| onClick | () => void | — | Click handler |