{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "dot-pattern-with-glow",
  "title": "Dot Pattern with Glow",
  "description": "A dot background that reveals a colored glow under the pointer.",
  "dependencies": [
    "cn@0.4.0"
  ],
  "registryDependencies": [
    "@gnosticai/dot-pattern"
  ],
  "files": [
    {
      "path": "packages/ui/src/components/patterns/dot-pattern-with-glow/dot-pattern-with-glow.astro",
      "content": "---\nimport type { HTMLAttributes } from \"astro/types\"\n\nimport { cn } from \"cn\"\n\nimport DotPattern from \"../dot-pattern/dot-pattern.astro\"\n\n/**\n * Options forwarded to the base dot layer.\n * `dotSize` and `dotSpace` also align the glow mask with those dots.\n */\ntype DotLayerProps = {\n  /** Radius of each base dot, in pixels. */\n  dotSize?: number\n  /** Distance from one base dot to the next, in pixels. */\n  dotSpace?: number\n  /** CSS color of each base dot. */\n  dotColor?: string\n  /** CSS color behind the base dots. */\n  dotBg?: string\n  /** Extra classes on the base dot layer. */\n  class?: string\n  /** Extra CSS declarations on the base dot layer. */\n  style?: HTMLAttributes<\"div\">[\"style\"]\n}\n\n/**\n * Dot background with a glow that follows the pointer.\n *\n * The base dots stay visible. A masked color layer fades in under the pointer\n * and shows that color only inside the dot shapes.\n */\ntype Props = HTMLAttributes<\"div\"> & {\n  /**\n   * CSS color of the glow.\n   * Uses `--color-brand-blue` when that token exists, otherwise `--color-primary`.\n   */\n  glowColor?: string\n  /**\n   * Radius of the glow, in pixels.\n   * @default 96\n   */\n  glowSize?: number\n  /**\n   * Opacity of the glow while the pointer is inside the container.\n   * @default 1\n   */\n  glowOpacity?: number\n  /**\n   * Radius of each dot in the glow mask, in pixels.\n   * @default 1.25\n   */\n  maskDotSize?: number\n  /** Props for the base dot layer. */\n  dotLayerProps?: DotLayerProps\n}\n\nconst {\n  glowColor = \"color-mix(in oklch, var(--color-brand-blue, var(--color-primary)) 92%, transparent)\",\n  glowSize = 96,\n  glowOpacity = 1,\n  maskDotSize = 1.25,\n  dotLayerProps,\n  class: className,\n  ...props\n} = Astro.props\n\nconst dotSpace = dotLayerProps?.dotSpace ?? 16\nconst dotSize = dotLayerProps?.dotSize ?? 1\n\n/** Shared variables. The script moves the glow by updating the inner layer. */\nconst layerStyle = [\n  `--agent-glow-color: ${glowColor}`,\n  \"--agent-glow-x: -1000px\",\n  \"--agent-glow-y: -1000px\",\n  \"--agent-glow-opacity: 0\",\n  `--agent-dot-space: ${dotSpace}px`,\n  `--agent-mask-dot-size: ${maskDotSize}px`,\n].join(\"; \")\n\n/** The mask repeats so the glow is visible only inside each dot. */\nconst glowStyle = [\n  \"opacity: var(--agent-glow-opacity)\",\n  `background: radial-gradient(circle ${glowSize}px at var(--agent-glow-x) var(--agent-glow-y), var(--agent-glow-color) 0%, var(--agent-glow-color) 25%, transparent 72%)`,\n  \"mask-image: radial-gradient(circle, black var(--agent-mask-dot-size), transparent var(--agent-mask-dot-size))\",\n  \"mask-size: var(--agent-dot-space) var(--agent-dot-space)\",\n  \"mask-position: center\",\n  \"-webkit-mask-image: radial-gradient(circle, black var(--agent-mask-dot-size), transparent var(--agent-mask-dot-size))\",\n  \"-webkit-mask-size: var(--agent-dot-space) var(--agent-dot-space)\",\n  \"-webkit-mask-position: center\",\n].join(\"; \")\n---\n\n<div\n  data-slot=\"dot-pattern-container\"\n  data-glow-opacity={glowOpacity}\n  class={cn(\"relative\", className)}\n  {...props}\n>\n  <slot />\n\n  <div\n    aria-hidden=\"true\"\n    data-slot=\"dot-pattern-glow-overlay\"\n    class=\"pointer-events-none absolute inset-0\"\n    style={layerStyle}\n  >\n    <DotPattern\n      dotSize={dotSize}\n      dotSpace={dotSpace}\n      dotColor={dotLayerProps?.dotColor}\n      dotBg={dotLayerProps?.dotBg}\n      class={dotLayerProps?.class}\n      style={dotLayerProps?.style}\n    />\n    <div\n      data-slot=\"dot-pattern-glow\"\n      class=\"absolute inset-0 transition-opacity duration-200\"\n      style={glowStyle}\n    ></div>\n  </div>\n</div>\n\n<script is:inline>\n  ;(() => {\n    const page = document.documentElement\n    if (page.dataset.gnosticaiDotPatternGlow != null) return\n    page.dataset.gnosticaiDotPatternGlow = \"true\"\n\n    /** Latest pointer position for each pattern, in container pixels. */\n    const points = new WeakMap()\n    /** Pending animation frame id for each pattern. */\n    const frames = new WeakMap()\n\n    const containerFrom = (event) => {\n      const target = event.target\n      if (!(target instanceof Element)) return null\n      const container = target.closest(\"[data-slot='dot-pattern-container']\")\n      return container instanceof HTMLElement ? container : null\n    }\n\n    const glowFrom = (container) => {\n      const glow = container.querySelector(\"[data-slot='dot-pattern-glow']\")\n      return glow instanceof HTMLElement ? glow : null\n    }\n\n    const paint = (container) => {\n      frames.delete(container)\n      const point = points.get(container)\n      const glow = glowFrom(container)\n      if (!point || !glow) return\n      glow.style.setProperty(\"--agent-glow-x\", `${point.x}px`)\n      glow.style.setProperty(\"--agent-glow-y\", `${point.y}px`)\n    }\n\n    const hide = (container) => {\n      points.delete(container)\n      const glow = glowFrom(container)\n      if (!glow) return\n      glow.style.setProperty(\"--agent-glow-opacity\", \"0\")\n    }\n\n    // pointerenter and pointerleave do not bubble, so listen during capture.\n    document.addEventListener(\n      \"pointerenter\",\n      (event) => {\n        const container = containerFrom(event)\n        const glow = container && glowFrom(container)\n        if (!container || !glow) return\n        glow.style.setProperty(\n          \"--agent-glow-opacity\",\n          container.dataset.glowOpacity || \"1\",\n        )\n      },\n      true,\n    )\n\n    document.addEventListener(\n      \"pointermove\",\n      (event) => {\n        const container = containerFrom(event)\n        if (!container) return\n        const rect = container.getBoundingClientRect()\n        points.set(container, {\n          x: event.clientX - rect.left,\n          y: event.clientY - rect.top,\n        })\n        if (frames.has(container)) return\n        frames.set(\n          container,\n          requestAnimationFrame(() => paint(container)),\n        )\n      },\n      { passive: true },\n    )\n\n    document.addEventListener(\n      \"pointerleave\",\n      (event) => {\n        const container = containerFrom(event)\n        if (!container) return\n        const next = event.relatedTarget\n        if (next instanceof Node && container.contains(next)) return\n        hide(container)\n      },\n      true,\n    )\n\n    window.addEventListener(\"blur\", () => {\n      document\n        .querySelectorAll(\"[data-slot='dot-pattern-container']\")\n        .forEach((container) => {\n          if (container instanceof HTMLElement) hide(container)\n        })\n    })\n  })()\n</script>\n",
      "type": "registry:file",
      "target": "~/src/components/patterns/dot-pattern-with-glow/dot-pattern-with-glow.astro"
    },
    {
      "path": "packages/ui/src/components/patterns/dot-pattern-with-glow/index.ts",
      "content": "export { default as DotPatternWithGlow } from './dot-pattern-with-glow.astro'\n",
      "type": "registry:file",
      "target": "~/src/components/patterns/dot-pattern-with-glow/index.ts"
    }
  ],
  "meta": {
    "framework": "astro",
    "source": "@gnosticai/ui"
  },
  "docs": "https://ui.gnosticai.com/patterns/dot-pattern-with-glow",
  "categories": [
    "patterns"
  ],
  "type": "registry:component"
}
