typography
Blur Reveal Code
An interactive, premium code element that reveals blurred digits sequentially as you glide your cursor over them.
textblurrevealinteractivehoverglide
▶Preview
Glide To Reveal Secret Code
- 0
- 3
- 4
- 8
- 7
- 2
🖲Usage
example-usage.tsx
import { BlurRevealCode } from "@/components/kanso/blur-reveal-code"
export default function BlurRevealCodeDemo() {
return (
<BlurRevealCode
code="034872"
label="Glide To Reveal Secret Code"
/>
)
}↓Installation
1
Create folder & copy source
Create a folder named kanso inside your project's components directory (i.e. components/kanso/). Copy the source code shown in the next section, and paste it into a file named blur-reveal-code.tsx inside it.
2
Required helper files
Ensure your project has the following helper files configured:
- →
lib/utils
<>Source Code
blur-reveal-code.tsx
'use client';
import * as React from 'react';
import { motion } from 'framer-motion';
import { cn } from '@/lib/utils';
interface BlurRevealCodeProps extends React.HTMLAttributes<HTMLDivElement> {
/** The code string to be revealed on hover */
code?: string;
/** The top label text */
label?: React.ReactNode;
className?: string;
}
const lerpValues = [1, 0.766, 0.707, 0.573, 0.422, 0.258];
function BlurRevealCode({
code = '034872',
label = 'Glide To Reveal Secret Code',
className,
...props
}: BlurRevealCodeProps) {
const [hoveredIndex, setHoveredIndex] = React.useState<number | null>(null);
const handlePointerMove = (e: React.PointerEvent<HTMLUListElement>) => {
// Only use elementsFromPoint for touch/pen to enable dragging over smoothly
if (e.pointerType === 'mouse') return;
const elements = document.elementsFromPoint(e.clientX, e.clientY);
const li = elements.find((el) => el.tagName === 'LI');
if (li && li.parentElement === e.currentTarget) {
const index = Array.from(e.currentTarget.children).indexOf(li);
if (index !== -1 && index !== hoveredIndex) {
setHoveredIndex(index);
}
}
};
const getActiveValue = (i: number) => {
if (hoveredIndex === null) return 0;
const dist = Math.abs(hoveredIndex - i);
return lerpValues[dist] || 0;
};
return (
<div
className={cn(
'flex flex-col items-center gap-10 sm:gap-14 w-full',
className
)}
{...props}
>
{label && (
<div className="text-2xl sm:text-4xl font-bold text-center bg-gradient-to-b from-zinc-700 to-zinc-400 dark:from-zinc-200 dark:to-zinc-500 bg-clip-text text-transparent tracking-tight">
{label}
</div>
)}
<ul
className={cn(
'relative flex flex-nowrap items-center justify-center rounded-3xl overflow-hidden touch-none',
'bg-white dark:bg-[#0a0a0b] cursor-crosshair',
'shadow-[0_1px_1px_rgba(0,0,0,0.05)_inset,0_8px_30px_rgba(0,0,0,0.05)] ring-1 ring-zinc-900/5',
'dark:shadow-[0_1px_1px_rgba(255,255,255,0.15)_inset,0_8px_30px_rgba(0,0,0,0.4)] dark:ring-white/10'
)}
onPointerLeave={() => setHoveredIndex(null)}
onPointerMove={handlePointerMove}
onPointerCancel={() => setHoveredIndex(null)}
>
{/* Subtle inner top highlight */}
<div className="absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-zinc-200 dark:via-white/20 to-transparent pointer-events-none" />
{code.split('').map((char, i) => {
const active = getActiveValue(i);
return (
<li
key={i}
tabIndex={0}
className={cn(
'relative flex h-full items-center justify-center outline-none select-none transition-colors duration-300',
'px-3 py-10 sm:px-6 sm:py-16', // Large interaction hit area
i === 0 && 'pl-8 sm:pl-16',
i === code.length - 1 && 'pr-8 sm:pr-16'
)}
onPointerEnter={(e) => {
if (e.pointerType === 'mouse') setHoveredIndex(i);
}}
onFocus={() => setHoveredIndex(i)}
>
<motion.span
className="inline-block text-5xl sm:text-7xl font-bold text-zinc-900 dark:text-zinc-50 drop-shadow-sm"
initial={false}
animate={{
scale: active + 0.5,
filter: `blur(${(1 - active) * 1.5}rem)`,
}}
transition={{
type: 'tween',
ease: [0.2, 0.8, 0.2, 1],
duration: 0.5 + (1 - active) * 0.5, // 0.5s for active, up to 1.0s for inactive
}}
>
{char}
</motion.span>
</li>
);
})}
</ul>
</div>
);
}
export { BlurRevealCode };
export type { BlurRevealCodeProps };