Hidden Text
Text that hides behind symbols until you hover over it. Perfect for creating mystery and surprise in your interface.
Install dependencies
npm i framer-motion
Utility function
Create a file lib/utils.ts and paste this code
import { ClassValue, clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
Hidden Text
Create a file hidden-text.tsx in your components folder and paste this code
'use client';
import React, { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { cn } from '@/lib/utils';
const HiddenText = ({
text,
symbol = '*',
delaySpeed = 0.025,
className = ''
}: {
text: string;
symbol?: string;
delaySpeed?: number;
className?: string;
}) => {
const [isHovered, setIsHovered] = useState(false);
const [firstTime, setFirstTime] = useState(true);
const [displayValue] = useState(text);
useEffect(() => {
if (firstTime) {
setFirstTime(false);
}
}, [firstTime]);
const handleCopy = () => {
navigator.clipboard.writeText(text);
};
return (
<ul
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
onClick={handleCopy}
className={cn(
'group flex gap-1 overflow-hidden items-center p-0 m-0 cursor-pointer',
className
)}
>
{displayValue.split(' ').map((word, i) => {
return (
<div key={'word-' + i} className="flex items-center">
{word.split('').map((char, j) => {
const delay =
(displayValue.split(' ').slice(0, i).join('').length + i + j) * delaySpeed;
return (
<AnimatePresence key={'char-' + j} mode="popLayout">
{isHovered ? (
<motion.li
key="char"
initial={{ y: 10, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: 10, opacity: 0 }}
transition={{
type: 'spring',
stiffness: 700,
damping: 30,
delay: delay
}}
>
{char}
</motion.li>
) : (
<motion.li
key="symbol"
initial={{ y: firstTime ? 0 : -10, opacity: firstTime ? 1 : 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: -10, opacity: 0 }}
transition={{
type: 'spring',
stiffness: 700,
damping: 30,
delay: delay
}}
>
{symbol}
</motion.li>
)}
</AnimatePresence>
);
})}
</div>
);
})}
</ul>
);
};
export default HiddenText;
Usage
<HiddenText text="hidden text" />