{"$schema":"https://ui.artbloom.tech/schema/registry-item.json","name":"text-shimmer","type":"registry:ui","title":"Text Shimmer","description":"A light band that sweeps across text, clipped to the glyphs themselves so it inherits weight and tracking.","author":"@artbloom","dependencies":["clsx@2.1.1","motion@13.2.0","tailwind-merge@3.6.0"],"registryDependencies":[],"files":[{"path":"components/ui/text-shimmer.tsx","target":"components/ui/text-shimmer.tsx","content":"\"use client\"\n\nimport type { CSSProperties } from \"react\"\nimport { motion, useReducedMotion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport type TextShimmerProps = {\n  children: string\n  className?: string\n  /** Seconds for one sweep. */\n  duration?: number\n  /** Width of the bright band, multiplied by character count. */\n  spread?: number\n}\n\n/**\n * A light band that sweeps across text. The gradient is painted into the text\n * itself via background-clip, so it inherits font weight and letter spacing\n * with no extra markup.\n *\n * Under `prefers-reduced-motion` the band parks mid-word instead of sweeping\n * forever. The treatment still reads as a highlight, and nothing moves — which\n * matters because an endless sweep is the exact thing the preference asks to be\n * spared, and `backgroundPosition` is not a transform, so Motion's own reduced\n * -motion handling would not have caught it.\n */\nexport function TextShimmer({\n  children,\n  className,\n  duration = 2,\n  spread = 2,\n}: TextShimmerProps) {\n  const reduced = useReducedMotion()\n\n  return (\n    <motion.span\n      className={cn(\n        \"relative inline-block bg-[length:250%_100%,auto] bg-clip-text text-transparent\",\n        \"[--base:#71717b] [--highlight:#09090b]\",\n        \"dark:[--base:#52525c] dark:[--highlight:#ffffff]\",\n        className,\n      )}\n      style={\n        {\n          \"--spread\": `${children.length * spread}px`,\n          backgroundImage:\n            \"linear-gradient(90deg, transparent calc(50% - var(--spread)), var(--highlight), transparent calc(50% + var(--spread))), linear-gradient(var(--base), var(--base))\",\n          backgroundRepeat: \"no-repeat, padding-box\",\n        } as CSSProperties\n      }\n      initial={{ backgroundPosition: reduced ? \"50% center\" : \"100% center\" }}\n      animate={{ backgroundPosition: reduced ? \"50% center\" : \"0% center\" }}\n      transition={reduced ? { duration: 0 } : { duration, ease: \"linear\", repeat: Infinity }}\n    >\n      {children}\n    </motion.span>\n  )\n}\n","type":"registry:ui"},{"path":"lib/utils.ts","target":"lib/utils.ts","content":"import { clsx, type ClassValue } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs))\n}\n","type":"registry:lib"}],"meta":{"kind":"animations","categories":["text"],"docs":"https://ui.artbloom.tech/artbloom/animations/text-shimmer"}}