Pinned text
It is a refined, minimal UI component built for Next.js and Framer Motion. A display card that blends bold, high-contrast aesthetics with fluid Framer Motion interactions. It features a tactile 'lift-on-hover' effect, reactive corner pins, and a dynamic shimmer overlay to ensure maximum visual impact.
Live Preview
Mritunjay Rai
Dependencies
npm install
framer-motionlucide-reactCode
"use client";
import React from "react";
import { motion } from "framer-motion";
const PinnedText = ({
text,
fontsize,
minWidth,
minHeight,
}: {
text?: string;
fontsize?: string;
minWidth?: number;
minHeight?: number;
}) => {
const pins = [
{ top: "-11px", left: "-11px", hover: { rotate: -15, scale: 1.25 } }, // Top Left
{ top: "-11px", right: "-11px", hover: { rotate: 15, scale: 1.25 } }, // Top Right
{ bottom: "-11px", left: "-11px", hover: { rotate: 15, scale: 1.25 } }, // Bottom Left
{ bottom: "-11px", right: "-11px", hover: { rotate: -15, scale: 1.25 } }, // Bottom Right
];
const dots = [
{
width: 48,
height: 48,
right: 40,
bottom: 20,
hoverTranslate: { x: -18, y: -16 },
scale: 1.6,
},
{
width: 28,
height: 28,
right: 120,
top: 18,
hoverTranslate: { x: 22, y: -10 },
scale: 1.3,
},
{
width: 18,
height: 18,
left: 80,
top: 10,
hoverTranslate: { x: 10, y: 18 },
scale: 1.1,
},
{
width: 36,
height: 36,
left: 30,
bottom: 16,
hoverTranslate: { x: -24, y: 14 },
scale: 1.8,
},
];
return (
<div className="w-full h-full flex items-center">
<motion.div
className="relative inline-block cursor-pointer"
initial="rest"
whileHover="hover"
whileTap="tap"
>
<motion.div
className="absolute left-5 top-5 w-full h-full bg-black"
variants={{
rest: { x: 0, y: 0, opacity: 1 },
hover: {
x: 6,
y: 6,
opacity: 0.75,
transition: { type: "spring", stiffness: 300, damping: 22 },
},
tap: {
x: 2,
y: 2,
transition: { type: "spring", stiffness: 400, damping: 25 },
},
}}
/>
<motion.div
className="relative bg-[#F24E1E] border-[3.5px] border-black overflow-hidden text-center"
style={{
minWidth: minWidth ? minWidth : 520,
minHeight: minHeight ? minHeight : 140,
padding: "1rem",
}}
variants={{
rest: { x: 0, y: 0 },
hover: {
x: -10,
y: -10,
transition: { type: "spring", stiffness: 300, damping: 22 },
},
tap: {
x: -4,
y: -4,
transition: { type: "spring", stiffness: 400, damping: 25 },
},
}}
>
<motion.div
className="absolute inset-0 pointer-events-none z-10"
style={{
background:
"linear-gradient(120deg, transparent 30%, rgba(255,255,255,0.18) 50%, transparent 70%)",
}}
variants={{
rest: { x: "-100%", opacity: 0 },
hover: {
x: ["−100%", "200%"],
opacity: [0, 1, 0],
transition: { duration: 0.7, ease: "easeInOut" },
},
}}
/>
{dots.map((dot, i) => {
const { hoverTranslate, scale, ...styles } = dot;
return (
<motion.div
key={i}
className="absolute rounded-full pointer-events-none"
style={{
width: styles.width,
height: styles.height,
...(styles.right !== undefined && { right: styles.right }),
...(styles.left !== undefined && { left: styles.left }),
...(styles.top !== undefined && { top: styles.top }),
...(styles.bottom !== undefined && { bottom: styles.bottom }),
background: "rgba(255,255,255,0.25)",
}}
variants={{
rest: { opacity: 0, x: 0, y: 0, scale: 1 },
hover: {
opacity: 1,
x: hoverTranslate.x,
y: hoverTranslate.y,
scale,
transition: {
type: "spring",
stiffness: 200,
damping: 18,
delay: i * 0.04,
},
},
}}
/>
);
})}
{pins.map((pin, i) => {
const { hover: hoverAnim, ...pos } = pin;
return (
<motion.div
key={i}
className="absolute z-10"
style={{
width: 20,
height: 20,
border: "3.5px solid #000",
...pos,
}}
variants={{
rest: { rotate: 0, scale: 1, background: "#ffffff" },
hover: {
rotate: hoverAnim.rotate,
scale: hoverAnim.scale,
background: "#FFE14D",
transition: {
type: "spring",
stiffness: 300,
damping: 18,
delay: i * 0.05,
},
},
}}
/>
);
})}
<motion.span
className="relative z-20 text-white font-black sekuya-regular"
style={{
fontSize: fontsize ? fontsize : "clamp(32px, 5vw, 52px)",
letterSpacing: "0.06em",
}}
variants={{
rest: { letterSpacing: "0.06em", textShadow: "none" },
hover: {
letterSpacing: "0.12em",
textShadow: "4px 4px 0 rgba(0,0,0,0.3)",
transition: { type: "spring", stiffness: 200, damping: 20 },
},
}}
>
{text || "Mritunjay Rai"}
</motion.span>
</motion.div>
</motion.div>
</div>
);
};
export default PinnedText;
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| text | string | 'Mritunjay Rai' | The primary display text centered within the card component. |
| fontsize | string | 'clamp(32px, 5vw, 52px)' | Responsive font size value, typically using a clamp function to scale between mobile and desktop. |
| minWidth | number | 520 | The minimum horizontal constraint of the card in pixels to maintain the Neobrutalist proportions. |
| minHeight | number | 140 | The minimum vertical constraint of the card in pixels to accommodate pins and decorative noise dots. |