{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "tactile-highlight", "title": "Tactile Highlight", "description": "A fluid text highlight component that reacts to viewport visibility and user interaction.", "dependencies": [ "framer-motion" ], "registryDependencies": [], "files": [ { "path": "registry/spark-ui/tactile-highlight.tsx", "content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport { motion, useInView, Variants } from \"framer-motion\";\nimport React, { useRef } from \"react\";\n\ntype HighlightDirection = \"left\" | \"right\" | \"top\" | \"bottom\";\n\ninterface TactileHighlightProps {\n children: React.ReactNode;\n className?: string;\n direction?: HighlightDirection;\n delay?: number;\n trigger?: \"auto\" | \"hover\" | \"inView\";\n}\n\n/**\n * TactileHighlight - A premium animated text highlight component.\n * Uses `mix-blend-difference` to guarantee mathematically perfect contrast\n * across both Light and Dark modes dynamically.\n */\nexport const TactileHighlight = ({\n children,\n className,\n direction = \"left\",\n delay = 0.1,\n trigger = \"inView\",\n}: TactileHighlightProps) => {\n const ref = useRef(null);\n // once: false allows the animation to elegantly restart when scrolling back into view\n const isInView = useInView(ref, { once: false, margin: \"-10%\" });\n\n const isAnimated = trigger === \"auto\" || (trigger === \"inView\" && isInView);\n\n const variants: Variants = {\n hidden: {\n scaleX: direction === \"left\" || direction === \"right\" ? 0 : 1,\n scaleY: direction === \"top\" || direction === \"bottom\" ? 0 : 1,\n originX: direction === \"left\" ? 0 : direction === \"right\" ? 1 : 0.5,\n originY: direction === \"top\" ? 0 : direction === \"bottom\" ? 1 : 0.5,\n borderRadius: \"12px\", // Starts very rounded\n },\n visible: {\n scaleX: 1,\n scaleY: 1,\n borderRadius: \"4px\", // Snaps to sharp corners\n transition: {\n type: \"spring\",\n damping: 22,\n stiffness: 130,\n mass: 0.8,\n delay: delay,\n },\n },\n hover: {\n scale: 1.05,\n rotate: direction === \"left\" ? -1.5 : direction === \"right\" ? 1.5 : 0,\n borderRadius: \"8px\",\n transition: {\n type: \"spring\",\n damping: 15,\n stiffness: 400,\n },\n },\n };\n\n return (\n \n {/* Background Block: Dark in light mode, Light in dark mode */}\n \n\n {/* \n Text Layer: Always white, but uses difference blending.\n Light Mode (White Page): White text difference White page = Black Text.\n Dark Mode (Black Page): White text difference Black page = White Text.\n Inside Highlight: Color perfectly inverts. \n */}\n \n {children}\n \n \n );\n};\n", "type": "registry:component" } ], "type": "registry:component" }