{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "glint-button",
  "type": "registry:component",
  "title": "Glint Button",
  "description": "A dark button with a glint that sweeps across it every so often and on hover, lighting up the fill, the border and the text at once.",
  "author": "Marcelo Treviño Velázquez <https://furin.dev>",
  "dependencies": [],
  "categories": [
    "Buttons"
  ],
  "docs": "Installed in components/furin/glint-button/. Docs, live controls and props table: https://furin.dev/components/glint-button/",
  "files": [
    {
      "path": "registry/glint-button/GlintButton.tsx",
      "content": "\"use client\";\nimport { useCallback, useEffect, useRef } from \"react\";\nimport type { ButtonHTMLAttributes, CSSProperties, PointerEvent, ReactNode } from \"react\";\nimport \"./GlintButton.css\";\n\nexport interface GlintButtonProps extends Omit<ButtonHTMLAttributes<HTMLButtonElement>, \"style\" | \"className\" | \"color\"> {\n  /** Texto del botón. */\n  children?: ReactNode;\n  /** Color del cuerpo del botón. */\n  color?: string;\n  /** Color del destello. */\n  glintColor?: string;\n  /** Cada cuántos segundos pasa el destello solo (0 = solo al pasar el cursor). */\n  interval?: number;\n  /** Segundos que tarda el destello en cruzar. */\n  speed?: number;\n  /** Inclinación del destello, en grados. */\n  angle?: number;\n  /** Muestra una flecha al final del texto. */\n  arrow?: boolean;\n  /** Alto del botón, en px. */\n  height?: number;\n  /** Clase extra para el botón. */\n  className?: string;\n  /** Estilos extra para el botón. */\n  style?: CSSProperties;\n}\n\n/** Botón oscuro con un destello que lo cruza cada tanto (y al pasar el cursor): se enciende el relleno, el borde y el texto en el mismo instante. */\nexport function GlintButton({\n  children = \"Get started\",\n  color = \"#16171c\",\n  glintColor = \"#ffffff\",\n  interval = 3.8,\n  speed = 1,\n  angle = 105,\n  arrow = true,\n  height = 60,\n  className = \"\",\n  style,\n  onPointerEnter,\n  ...rest\n}: GlintButtonProps) {\n  const ref = useRef<HTMLButtonElement>(null);\n  const reduced = useRef(false);\n  const timer = useRef(0);\n  const cfg = useRef({ interval, speed });\n  cfg.current = { interval, speed };\n\n  /** Dispara un destello (reiniciándolo si ya había uno en curso). */\n  const fire = useCallback(() => {\n    const el = ref.current;\n    if (!el || reduced.current) return;\n    el.removeAttribute(\"data-glint\");\n    void el.offsetWidth;\n    el.setAttribute(\"data-glint\", \"true\");\n  }, []);\n\n  useEffect(() => {\n    reduced.current = window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n    const el = ref.current;\n    if (!el || reduced.current) return;\n    let visible = true;\n    const io = new IntersectionObserver((e) => { visible = e[0]?.isIntersecting ?? true; });\n    io.observe(el);\n    const loop = (first: boolean) => {\n      timer.current = window.setTimeout(() => {\n        if (visible && !document.hidden) fire();\n        if (cfg.current.interval > 0) loop(false);\n      }, first ? 700 : Math.max(0.8, cfg.current.interval) * 1000);\n    };\n    if (interval > 0) loop(true);\n    return () => { window.clearTimeout(timer.current); io.disconnect(); };\n  }, [interval, fire]);\n\n  const vars = {\n    \"--furin-glint-button-color\": color,\n    \"--furin-glint-button-glint\": glintColor,\n    \"--furin-glint-button-dur\": `${0.95 / Math.max(0.2, speed)}s`,\n    \"--furin-glint-button-angle\": `${angle}deg`,\n    \"--furin-glint-button-h\": `${height}px`,\n  } as CSSProperties;\n\n  return (\n    <button\n      ref={ref}\n      type=\"button\"\n      className={`furin-glint-button ${className}`}\n      style={{ ...vars, ...style }}\n      onPointerEnter={(e: PointerEvent<HTMLButtonElement>) => { if (e.pointerType === \"mouse\") fire(); onPointerEnter?.(e); }}\n      onAnimationEnd={(e) => { if (e.target === e.currentTarget || (e.target as HTMLElement).classList.contains(\"furin-glint-button__fill\")) ref.current?.removeAttribute(\"data-glint\"); }}\n      {...rest}\n    >\n      <span className=\"furin-glint-button__fill\" aria-hidden=\"true\" />\n      <span className=\"furin-glint-button__edge\" aria-hidden=\"true\" />\n      <span className=\"furin-glint-button__label\">{children}</span>\n      {arrow ? (\n        <svg className=\"furin-glint-button__arrow\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\"><path d=\"M5 12h13M13 6.5 18.5 12 13 17.5\" /></svg>\n      ) : null}\n    </button>\n  );\n}\n\nexport default GlintButton;\n",
      "type": "registry:component",
      "target": "components/furin/glint-button/GlintButton.tsx"
    },
    {
      "path": "registry/glint-button/GlintButton.css",
      "content": ".furin-glint-button {\n  position: relative;\n  display: inline-flex;\n  align-items: center;\n  gap: 10px;\n  height: var(--furin-glint-button-h);\n  padding: 0 32px 0 34px;\n  border: 0;\n  border-radius: 999px;\n  overflow: hidden;\n  isolation: isolate;\n  color: #fff;\n  background:\n    radial-gradient(120% 140% at 20% -40%, rgb(255 255 255 / 0.16), transparent 55%),\n    linear-gradient(180deg, color-mix(in srgb, var(--furin-glint-button-color) 78%, #fff) 0%, var(--furin-glint-button-color) 48%, color-mix(in srgb, var(--furin-glint-button-color) 70%, #000) 100%);\n  box-shadow:\n    inset 0 1px 0 rgb(255 255 255 / 0.18),\n    inset 0 -1px 0 rgb(0 0 0 / 0.5),\n    0 1px 2px rgb(0 0 0 / 0.5),\n    0 16px 28px -12px rgb(0 0 0 / 0.85);\n  font: 600 17px/1 Geist, -apple-system, BlinkMacSystemFont, \"SF Pro Text\", system-ui, sans-serif;\n  letter-spacing: -0.01em;\n  cursor: pointer;\n  user-select: none;\n  -webkit-tap-highlight-color: transparent;\n  outline: none;\n  transform: scale(1);\n  transition: transform 0.5s cubic-bezier(0.3, 1.7, 0.5, 1);\n}\n.furin-glint-button:hover { transform: scale(1.03); }\n.furin-glint-button:active { transform: scale(0.96); transition-duration: 0.1s; }\n.furin-glint-button:focus-visible { box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.18), 0 0 0 4px rgb(255 255 255 / 0.5); }\n.furin-glint-button:disabled { opacity: 0.45; cursor: not-allowed; }\n\n/* Las tres capas comparten la misma franja y la misma animación: el destello las enciende a la vez. */\n.furin-glint-button__fill,\n.furin-glint-button__edge,\n.furin-glint-button__label {\n  --furin-glint-button-band: linear-gradient(var(--furin-glint-button-angle),\n    transparent 43%,\n    color-mix(in srgb, var(--furin-glint-button-glint) 12%, transparent) 47%,\n    color-mix(in srgb, var(--furin-glint-button-glint) 90%, transparent) 50%,\n    color-mix(in srgb, var(--furin-glint-button-glint) 12%, transparent) 53%,\n    transparent 57%);\n}\n.furin-glint-button__fill {\n  position: absolute;\n  inset: 0;\n  z-index: -1;\n  border-radius: inherit;\n  pointer-events: none;\n  opacity: 0.38;\n  mix-blend-mode: screen;\n  background: var(--furin-glint-button-band) 130% 0 / 260% 100% no-repeat;\n}\n/* Borde que se enciende: el mismo destello, recortado a un aro de 1.5px. */\n.furin-glint-button__edge {\n  position: absolute;\n  inset: 0;\n  border-radius: inherit;\n  padding: 1.5px;\n  pointer-events: none;\n  background:\n    var(--furin-glint-button-band) 130% 0 / 260% 100% no-repeat,\n    linear-gradient(180deg, rgb(255 255 255 / 0.3), rgb(255 255 255 / 0.04) 55%, rgb(255 255 255 / 0.12));\n  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);\n  -webkit-mask-composite: xor;\n  mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);\n}\n.furin-glint-button__label {\n  position: relative;\n  color: transparent;\n  background: var(--furin-glint-button-band) 130% 0 / 260% 100% no-repeat, linear-gradient(#dfe2ea, #dfe2ea);\n  -webkit-background-clip: text;\n  background-clip: text;\n  text-shadow: none;\n  filter: drop-shadow(0 1px 0 rgb(0 0 0 / 0.5));\n}\n.furin-glint-button__arrow { width: 18px; height: 18px; flex: none; color: #dfe2ea; transition: transform 0.4s cubic-bezier(0.3, 1.7, 0.5, 1); }\n.furin-glint-button:hover .furin-glint-button__arrow { transform: translateX(3px); }\n\n.furin-glint-button[data-glint=\"true\"] .furin-glint-button__fill,\n.furin-glint-button[data-glint=\"true\"] .furin-glint-button__edge,\n.furin-glint-button[data-glint=\"true\"] .furin-glint-button__label {\n  animation: furin-glint-button-sweep var(--furin-glint-button-dur) cubic-bezier(0.35, 0.05, 0.3, 1) both;\n}\n.furin-glint-button[data-glint=\"true\"] .furin-glint-button__fill { opacity: 0.6; }\n@keyframes furin-glint-button-sweep {\n  from { background-position: 130% 0, 0 0; }\n  to { background-position: -30% 0, 0 0; }\n}\n@media (prefers-reduced-motion: reduce) { .furin-glint-button { transition: none; } .furin-glint-button__arrow { transition: none; } }\n",
      "type": "registry:file",
      "target": "components/furin/glint-button/GlintButton.css"
    }
  ]
}
