Rotating text button animation

tsx
const [ButtonContext, useButtonContext] = createContext<ButtonContextProps>();

const Button = (props: ButtonProps) => {
  const { className, ...rest } = props;

  const [isHovered, setIsHovered] = useState(false);

  return (
    <ButtonContext value={{ isHovered }}>
      <Box
        as={motion.button}
        className={cn(buttonBaseStyles(), className)}
        onMouseEnter={() => setIsHovered(true)}
        onMouseLeave={() => setIsHovered(false)}
        {...rest}
      />
    </ButtonContext>
  );
};
tsx
const ButtonRotatingText = (props: ButtonRotatingTextProps) => {
  const { label } = props;

  const { isHovered } = useButtonContext();

  return (
    <motion.div className="relative overflow-hidden">
      <motion.div
        animate={{ y: isHovered ? "-100%" : "0%" }}
        transition={{ duration: 0.6, ease: [0.77, 0, 0.175, 1] }}
      >
        {label}
      </motion.div>
      <motion.div
        initial={{ y: "100%" }}
        animate={{ y: isHovered ? "0%" : "100%" }}
        transition={{ duration: 0.6, ease: [0.77, 0, 0.175, 1] }}
        className="absolute top-0"
      >
        {label}
      </motion.div>
    </motion.div>
  );
};

Usage example

tsx
<Button className="relative">
  <ButtonRotatingText label="Hover me" />
</Button>