Tooltip
Pop-Brutalist high-contrast tooltip with hard shadow.
components/ui/tooltip.tsx
import * as React from 'react'
import { motion, AnimatePresence } from 'motion/react'
import { cn } from '@/lib/utils'
export interface TooltipProps {
content: React.ReactNode
children: React.ReactNode
position?: 'top' | 'bottom' | 'left' | 'right'
className?: string
}
export function Tooltip({
content,
children,
position = 'top',
className,
}: TooltipProps) {
const [visible, setVisible] = React.useState(false)
const positionStyles = {
top: 'bottom-full left-1/2 -translate-x-1/2 mb-2',
bottom: 'top-full left-1/2 -translate-x-1/2 mt-2',
left: 'right-full top-1/2 -translate-y-1/2 mr-2',
right: 'left-full top-1/2 -translate-y-1/2 ml-2',
}
const animVariants = {
top: { initial: { opacity: 0, y: 5, scale: 0.9 }, animate: { opacity: 1, y: 0, scale: 1 } },
bottom: { initial: { opacity: 0, y: -5, scale: 0.9 }, animate: { opacity: 1, y: 0, scale: 1 } },
left: { initial: { opacity: 0, x: 5, scale: 0.9 }, animate: { opacity: 1, x: 0, scale: 1 } },
right: { initial: { opacity: 0, x: -5, scale: 0.9 }, animate: { opacity: 1, x: 0, scale: 1 } },
}
return (
<div
className="relative inline-block"
onMouseEnter={() => setVisible(true)}
onMouseLeave={() => setVisible(false)}
onFocus={() => setVisible(true)}
onBlur={() => setVisible(false)}
>
{children}
<AnimatePresence>
{visible && (
<motion.div
initial={animVariants[position].initial}
animate={animVariants[position].animate}
exit={animVariants[position].initial}
transition={{ type: 'spring', stiffness: 500, damping: 25 }}
className={cn(
'absolute z-50 whitespace-nowrap bg-rk-ink text-rk-primary text-xs font-mono font-bold px-3.5 py-1.5 rounded-xl rk-border-sm rk-shadow-md pointer-events-none',
positionStyles[position],
className
)}
>
{content}
</motion.div>
)}
</AnimatePresence>
</div>
)
}
- Visual style
- brutalist
- Interaction
- tooltip
DependenciesBundle weight · light
clsxtailwind-merge