{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"shimmer-button","type":"registry:ui","title":"ShimmerButton","description":"Pill button with a satin sheen: a soft band of light sweeps across the face and lifts the label, then rests; on hover the highlight follows the pointer","author":"fancy-ui <https://fancy-ui.rama.app>","categories":["buttons"],"registryDependencies":["https://fancy-ui.rama.app/r/internals-dom.json","https://fancy-ui.rama.app/r/sound.json","https://fancy-ui.rama.app/r/utils.json"],"files":[{"path":"react/src/components/shimmer-button/ShimmerButton.tsx","content":"\"use client\";\n\nimport { forwardRef } from \"react\";\nimport type {\n\tButtonHTMLAttributes,\n\tCSSProperties,\n\tMouseEvent,\n\tPointerEvent,\n\tReactNode,\n} from \"react\";\nimport { cn } from \"../../utils\";\nimport { useSoundCue } from \"../../sound/use-sound\";\nimport { useSoundFeedback } from \"../../sound/sound-feedback\";\nimport { useComposedRefs } from \"../../internals/dom/use-composed-refs\";\nimport \"./shimmer-button.css\";\n\ntype BaseProps = {\n\t/** Colour of the sheen */\n\tshimmerColor?: string;\n\t/** Thickness of the rim the sheen glints on */\n\tshimmerSize?: string;\n\t/** Button border radius */\n\tborderRadius?: string;\n\t/** Duration of one sheen cycle: the sweep, then a pause */\n\tshimmerDuration?: string;\n\t/** Button background color */\n\tbackground?: string;\n\t/** Custom CSS class */\n\tclassName?: string;\n\t/** Button content */\n\tchildren?: ReactNode;\n\t/**\n\t * Plays the `hover` cue on mouse/pen pointer enter and the `press` cue on\n\t * click. Off by default; only audible once the user has enabled sound.\n\t */\n\tsound?: boolean;\n};\n\nexport interface ShimmerButtonProps\n\textends BaseProps, Omit<ButtonHTMLAttributes<HTMLButtonElement>, keyof BaseProps> {}\n\nexport const ShimmerButton = forwardRef<HTMLButtonElement, ShimmerButtonProps>(\n\t(\n\t\t{\n\t\t\tclassName,\n\t\t\tshimmerColor = \"#ffffff\",\n\t\t\tshimmerSize = \"0.05em\",\n\t\t\tborderRadius = \"100px\",\n\t\t\tshimmerDuration = \"3s\",\n\t\t\tbackground = \"rgba(0, 0, 0, 1)\",\n\t\t\tchildren,\n\t\t\tonClick,\n\t\t\tonPointerMove,\n\t\t\tsound = false,\n\t\t\t...restProps\n\t\t},\n\t\tref\n\t) => {\n\t\tconst styleVars = {\n\t\t\t\"--shimmer-color\": shimmerColor,\n\t\t\t\"--radius\": borderRadius,\n\t\t\t\"--speed\": shimmerDuration,\n\t\t\t\"--cut\": shimmerSize,\n\t\t\t\"--bg\": background,\n\t\t} as CSSProperties;\n\n\t\tconst playCue = useSoundCue(sound);\n\n\t\t// Hover cue for mouse/pen pointers only. `on: {}` binds nothing when sound is\n\t\t// off (and never the default click → press, which `handleClick` already plays).\n\t\tconst soundRef = useSoundFeedback({\n\t\t\ton: sound ? { pointerenter: \"hover\" } : {},\n\t\t\tdisabled: restProps.disabled,\n\t\t});\n\t\tconst composedRef = useComposedRefs(ref, soundRef);\n\n\t\tfunction handleClick(event: MouseEvent<HTMLButtonElement>) {\n\t\t\tif (sound && !restProps.disabled) playCue(\"press\");\n\t\t\tonClick?.(event);\n\t\t}\n\n\t\t// The hover sheen follows the pointer: two CSS variables, no re-render.\n\t\tfunction handlePointerMove(event: PointerEvent<HTMLButtonElement>) {\n\t\t\tconst el = event.currentTarget;\n\t\t\tconst rect = el.getBoundingClientRect();\n\t\t\tel.style.setProperty(\"--mx\", `${event.clientX - rect.left}px`);\n\t\t\tel.style.setProperty(\"--my\", `${event.clientY - rect.top}px`);\n\t\t\tonPointerMove?.(event);\n\t\t}\n\n\t\treturn (\n\t\t\t<button\n\t\t\t\tref={composedRef}\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"shimmer-button group relative isolate flex cursor-pointer items-center justify-center overflow-hidden [border-radius:var(--radius)] px-6 py-3 whitespace-nowrap text-white/90\",\n\t\t\t\t\t\"transform-gpu duration-300 ease-out hover:text-white active:scale-[0.98] transition-[color] motion-safe:transition-[transform,color]\",\n\t\t\t\t\tclassName\n\t\t\t\t)}\n\t\t\t\tstyle={styleVars}\n\t\t\t\tonClick={handleClick}\n\t\t\t\tonPointerMove={handlePointerMove}\n\t\t\t\t{...restProps}\n\t\t\t>\n\t\t\t\t{/* Rim: shows around the face, catches the sheen as it passes */}\n\t\t\t\t<span className=\"shimmer-button__rim\" aria-hidden=\"true\"></span>\n\n\t\t\t\t{/* Face */}\n\t\t\t\t<span className=\"shimmer-button__face\" aria-hidden=\"true\"></span>\n\n\t\t\t\t{/* Content */}\n\t\t\t\t<span className=\"relative z-10\">{children}</span>\n\n\t\t\t\t{/* Sweep: a satin band that crosses the button, then rests */}\n\t\t\t\t<span className=\"shimmer-button__sheen\" aria-hidden=\"true\"></span>\n\n\t\t\t\t{/* Hover: the sheen follows the pointer */}\n\t\t\t\t<span className=\"shimmer-button__spot\" aria-hidden=\"true\"></span>\n\t\t\t</button>\n\t\t);\n\t}\n);\n\nShimmerButton.displayName = \"ShimmerButton\";\n","type":"registry:ui","target":"@ui/fancy/components/shimmer-button/ShimmerButton.tsx"},{"path":"react/src/components/shimmer-button/index.ts","content":"\"use client\";\n\nexport { ShimmerButton } from \"./ShimmerButton\";\nexport type { ShimmerButtonProps } from \"./ShimmerButton\";\n","type":"registry:ui","target":"@ui/fancy/components/shimmer-button/index.ts"},{"path":"react/src/components/shimmer-button/shimmer-button.css","content":"/* Plain CSS is global: every selector carries the component prefix (.shimmer-button or .shimmer-button__*), so nothing leaks onto consumer elements. */\n.shimmer-button__rim,\n.shimmer-button__face,\n.shimmer-button__sheen,\n.shimmer-button__spot {\n\tposition: absolute;\n\tpointer-events: none;\n\tborder-radius: inherit;\n}\n\n.shimmer-button__rim {\n\tinset: 0;\n\tz-index: -2;\n\tbackground: linear-gradient(\n\t\tto bottom,\n\t\tcolor-mix(in srgb, var(--shimmer-color) 28%, transparent),\n\t\tcolor-mix(in srgb, var(--shimmer-color) 6%, transparent) 55%,\n\t\tcolor-mix(in srgb, var(--shimmer-color) 14%, transparent)\n\t);\n}\n\n.shimmer-button__face {\n\tinset: var(--cut);\n\tz-index: -1;\n\tborder-radius: calc(var(--radius) - var(--cut));\n\tbackground:\n\t\tlinear-gradient(\n\t\t\tto bottom,\n\t\t\tcolor-mix(in srgb, var(--shimmer-color) 10%, transparent),\n\t\t\ttransparent 50%\n\t\t),\n\t\tvar(--bg);\n\tbox-shadow: inset 0 -10px 16px -12px color-mix(in srgb, var(--shimmer-color) 30%, transparent);\n}\n\n/* Screen blend: the band lightens the face and lifts the label to full white. */\n.shimmer-button__sheen {\n\tinset: 0;\n\tz-index: 20;\n\tmix-blend-mode: screen;\n\t/* a wide soft halo with a thin brighter core */\n\tbackground:\n\t\tlinear-gradient(\n\t\t\t108deg,\n\t\t\ttransparent 44%,\n\t\t\tcolor-mix(in srgb, var(--shimmer-color) 38%, transparent) 50%,\n\t\t\ttransparent 56%\n\t\t),\n\t\tlinear-gradient(\n\t\t\t108deg,\n\t\t\ttransparent 30%,\n\t\t\tcolor-mix(in srgb, var(--shimmer-color) 12%, transparent) 42%,\n\t\t\tcolor-mix(in srgb, var(--shimmer-color) 18%, transparent) 50%,\n\t\t\tcolor-mix(in srgb, var(--shimmer-color) 12%, transparent) 58%,\n\t\t\ttransparent 70%\n\t\t);\n\tbackground-size: 250% 100%;\n\tbackground-repeat: no-repeat;\n\tbackground-position: 100% 0;\n\tanimation: shimmer-sweep var(--speed) cubic-bezier(0.4, 0, 0.2, 1) infinite;\n}\n\n.shimmer-button__spot {\n\tinset: 0;\n\tz-index: 20;\n\tmix-blend-mode: screen;\n\topacity: 0;\n\tbackground: radial-gradient(\n\t\t90px circle at var(--mx, 50%) var(--my, 50%),\n\t\tcolor-mix(in srgb, var(--shimmer-color) 34%, transparent),\n\t\ttransparent 70%\n\t);\n}\n\n.shimmer-button:hover .shimmer-button__spot {\n\topacity: 1;\n}\n\n/* The pointer takes over from the sweep while hovering. */\n.shimmer-button:hover .shimmer-button__sheen {\n\topacity: 0;\n}\n\n.shimmer-button:focus-visible {\n\toutline: 2px solid color-mix(in srgb, var(--shimmer-color) 70%, transparent);\n\toutline-offset: 2px;\n}\n\n.shimmer-button:disabled {\n\tcursor: not-allowed;\n\topacity: 0.5;\n}\n\n.shimmer-button:disabled .shimmer-button__sheen,\n.shimmer-button:disabled .shimmer-button__spot {\n\tdisplay: none;\n}\n\n/* Sweep across in the first 55% of the cycle, then rest off-button. */\n@keyframes shimmer-sweep {\n\t0% {\n\t\tbackground-position: 100% 0;\n\t}\n\t55%,\n\t100% {\n\t\tbackground-position: 0% 0;\n\t}\n}\n\n@media (prefers-reduced-motion: reduce) {\n\t.shimmer-button__sheen {\n\t\tanimation: none;\n\t\tbackground-position: 38% 0;\n\t\topacity: 0.6;\n\t}\n}\n\n/* The fade is motion: it only runs when the visitor has not asked for less. */\n@media (prefers-reduced-motion: no-preference) {\n\t.shimmer-button__sheen {\n\t\ttransition: opacity 0.3s ease;\n\t}\n\t.shimmer-button__spot {\n\t\ttransition: opacity 0.3s ease;\n\t}\n}\n","type":"registry:ui","target":"@ui/fancy/components/shimmer-button/shimmer-button.css"}],"meta":{"license":"MIT","package":"fancy-ui-react","version":"0.7.0","source":"https://fancy-ui.rama.app/docs/components/shimmer-button?framework=react"}}