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 installframer-motionlucide-react

Code

"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

PropTypeDefaultDescription
textstring'Mritunjay Rai'The primary display text centered within the card component.
fontsizestring'clamp(32px, 5vw, 52px)'Responsive font size value, typically using a clamp function to scale between mobile and desktop.
minWidthnumber520The minimum horizontal constraint of the card in pixels to maintain the Neobrutalist proportions.
minHeightnumber140The minimum vertical constraint of the card in pixels to accommodate pins and decorative noise dots.