{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "hexagon-pattern",
  "type": "registry:ui",
  "title": "Hexagon Pattern",
  "description": "A background hexagon pattern made with SVGs, fully customizable using Tailwind CSS.",
  "files": [
    {
      "path": "registry/magicui/hexagon-pattern.tsx",
      "content": "import { useId } from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\ninterface HexagonPatternProps extends React.SVGProps<SVGSVGElement> {\n  /**\n   * The radius of each hexagon (center to vertex).\n   * @default 40\n   */\n  radius?: number\n  /**\n   * Spacing in pixels between adjacent hexagons.\n   * The tile grows by this amount while the visual radius stays fixed,\n   * so the gap is evenly distributed on all sides of each hexagon.\n   * @default 0\n   */\n  gap?: number\n  /**\n   * Offset applied to the pattern origin on the x-axis.\n   * @default -1\n   */\n  x?: number\n  /**\n   * Offset applied to the pattern origin on the y-axis.\n   * @default -1\n   */\n  y?: number\n  /**\n   * Controls the orientation of the hexagons.\n   * - `\"horizontal\"` — flat-top hexagons tiled in a horizontal honeycomb grid.\n   * - `\"vertical\"` — pointy-top hexagons tiled in a vertical honeycomb grid.\n   * @default \"horizontal\"\n   */\n  direction?: \"horizontal\" | \"vertical\"\n  /**\n   * SVG stroke-dasharray applied to each hexagon outline.\n   * @default \"0\"\n   */\n  strokeDasharray?: string\n  /**\n   * Array of [col, row] coordinates for hexagons that should be highlighted\n   * (filled) on top of the repeating pattern — mirrors the `squares` prop of\n   * GridPattern.\n   */\n  hexagons?: Array<[col: number, row: number]>\n  className?: string\n  [key: string]: unknown\n}\n\ntype HexPoint = readonly [number, number]\n\nfunction hexVertexList(\n  cx: number,\n  cy: number,\n  r: number,\n  direction: \"horizontal\" | \"vertical\"\n): HexPoint[] {\n  const startAngle = direction === \"horizontal\" ? 0 : 30\n  return Array.from({ length: 6 }, (_, i) => {\n    const angle = ((startAngle + i * 60) * Math.PI) / 180\n    return [cx + r * Math.cos(angle), cy + r * Math.sin(angle)] as const\n  })\n}\n\nfunction hexPoints(\n  cx: number,\n  cy: number,\n  r: number,\n  direction: \"horizontal\" | \"vertical\"\n): string {\n  return hexVertexList(cx, cy, r, direction)\n    .map(([px, py]) => `${px},${py}`)\n    .join(\" \")\n}\n\nfunction edgeLexKey(a: HexPoint, b: HexPoint): string {\n  const [p, q] =\n    a[0] < b[0] || (a[0] === b[0] && a[1] <= b[1]) ? [a, b] : [b, a]\n  return `${p[0].toFixed(6)},${p[1].toFixed(6)}|${q[0].toFixed(6)},${q[1].toFixed(6)}`\n}\n\nfunction collectUniqueHexEdges(\n  centers: [number, number][],\n  r: number,\n  direction: \"horizontal\" | \"vertical\"\n): [HexPoint, HexPoint][] {\n  const seen = new Set<string>()\n  const edges: [HexPoint, HexPoint][] = []\n  for (const [cx, cy] of centers) {\n    const verts = hexVertexList(cx, cy, r, direction)\n    for (let i = 0; i < 6; i++) {\n      const a = verts[i]\n      const b = verts[(i + 1) % 6]\n      const key = edgeLexKey(a, b)\n      if (!seen.has(key)) {\n        seen.add(key)\n        edges.push([a, b])\n      }\n    }\n  }\n  return edges\n}\n\nfunction isSolidStrokeDasharray(strokeDasharray: string): boolean {\n  const t = strokeDasharray.trim()\n  return t === \"\" || t === \"none\" || t === \"0\"\n}\n\nfunction getHexSpacing(\n  r: number,\n  direction: \"horizontal\" | \"vertical\",\n  gap: number\n): {\n  colStep: number\n  rowStep: number\n  tileW: number\n  tileH: number\n} {\n  const sqrt3 = Math.sqrt(3)\n\n  // `gap` should match the visible edge-to-edge spacing, so we add it along\n  // the shared-edge normal instead of directly on the raw x/y axes.\n  if (direction === \"horizontal\") {\n    const colStep = (3 * r) / 2 + (sqrt3 * gap) / 2\n    const rowStep = sqrt3 * r + gap\n\n    return {\n      colStep,\n      rowStep,\n      tileW: colStep * 2,\n      tileH: rowStep,\n    }\n  }\n\n  const colStep = sqrt3 * r + gap\n  const rowStep = (3 * r) / 2 + (sqrt3 * gap) / 2\n\n  return {\n    colStep,\n    rowStep,\n    tileW: colStep,\n    tileH: rowStep * 2,\n  }\n}\n\nfunction getTileGeometry(\n  r: number,\n  direction: \"horizontal\" | \"vertical\",\n  gap: number\n): {\n  tileW: number\n  tileH: number\n  centers: [number, number][]\n} {\n  if (direction === \"horizontal\") {\n    const { colStep, rowStep, tileW, tileH } = getHexSpacing(r, direction, gap)\n\n    const canonical: [number, number][] = [\n      [colStep / 2, rowStep / 2],\n      [(colStep * 3) / 2, rowStep],\n    ]\n\n    const centers: [number, number][] = []\n    for (const [cx, cy] of canonical) {\n      centers.push([cx, cy])\n      if (cy - r < 0) centers.push([cx, cy + tileH])\n      if (cy + r > tileH) centers.push([cx, cy - tileH])\n      if (cx - r < 0) centers.push([cx + tileW, cy])\n      if (cx + r > tileW) centers.push([cx - tileW, cy])\n      if (cy - r < 0 && cx - r < 0) centers.push([cx + tileW, cy + tileH])\n      if (cy - r < 0 && cx + r > tileW) centers.push([cx - tileW, cy + tileH])\n      if (cy + r > tileH && cx - r < 0) centers.push([cx + tileW, cy - tileH])\n      if (cy + r > tileH && cx + r > tileW)\n        centers.push([cx - tileW, cy - tileH])\n    }\n\n    return { tileW, tileH, centers }\n  } else {\n    const { colStep, rowStep, tileW, tileH } = getHexSpacing(r, direction, gap)\n\n    const canonical: [number, number][] = [\n      [colStep / 2, rowStep / 2],\n      [colStep, (rowStep * 3) / 2],\n    ]\n\n    const centers: [number, number][] = []\n    for (const [cx, cy] of canonical) {\n      centers.push([cx, cy])\n      if (cy - r < 0) centers.push([cx, cy + tileH])\n      if (cy + r > tileH) centers.push([cx, cy - tileH])\n      if (cx - r < 0) centers.push([cx + tileW, cy])\n      if (cx + r > tileW) centers.push([cx - tileW, cy])\n      if (cy - r < 0 && cx - r < 0) centers.push([cx + tileW, cy + tileH])\n      if (cy - r < 0 && cx + r > tileW) centers.push([cx - tileW, cy + tileH])\n      if (cy + r > tileH && cx - r < 0) centers.push([cx + tileW, cy - tileH])\n      if (cy + r > tileH && cx + r > tileW)\n        centers.push([cx - tileW, cy - tileH])\n    }\n\n    return { tileW, tileH, centers }\n  }\n}\n\nfunction hexCenter(\n  col: number,\n  row: number,\n  r: number,\n  direction: \"horizontal\" | \"vertical\",\n  gap: number\n): [number, number] {\n  if (direction === \"horizontal\") {\n    const { colStep, rowStep } = getHexSpacing(r, direction, gap)\n    const x = col * colStep + colStep / 2\n    const y = row * rowStep + rowStep / 2 + (col % 2 !== 0 ? rowStep / 2 : 0)\n    return [x, y]\n  } else {\n    const { colStep, rowStep } = getHexSpacing(r, direction, gap)\n    const x = col * colStep + colStep / 2 + (row % 2 !== 0 ? colStep / 2 : 0)\n    const y = row * rowStep + rowStep / 2\n    return [x, y]\n  }\n}\n\nexport function HexagonPattern({\n  radius = 40,\n  gap = 0,\n  x = -1,\n  y = -1,\n  strokeDasharray = \"0\",\n  direction = \"horizontal\",\n  hexagons,\n  className,\n  ...props\n}: HexagonPatternProps) {\n  const id = useId()\n\n  const { tileW, tileH, centers } = getTileGeometry(radius, direction, gap)\n  const solidStroke = isSolidStrokeDasharray(strokeDasharray)\n  const dashedEdges = solidStroke\n    ? null\n    : collectUniqueHexEdges(centers, radius, direction)\n\n  return (\n    <svg\n      aria-hidden=\"true\"\n      className={cn(\n        \"pointer-events-none absolute inset-0 h-full w-full fill-gray-400/30 stroke-gray-400/30\",\n        className\n      )}\n      {...props}\n    >\n      <defs>\n        <pattern\n          id={id}\n          width={tileW}\n          height={tileH}\n          patternUnits=\"userSpaceOnUse\"\n          x={x}\n          y={y}\n        >\n          {solidStroke\n            ? centers.map(([cx, cy]) => (\n                <polygon\n                  className=\"fill-none\"\n                  key={`${cx}-${cy}`}\n                  points={hexPoints(cx, cy, radius, direction)}\n                  strokeDasharray={strokeDasharray}\n                />\n              ))\n            : dashedEdges?.map(([a, b]) => (\n                <line\n                  className=\"fill-none\"\n                  key={edgeLexKey(a, b)}\n                  x1={a[0]}\n                  x2={b[0]}\n                  y1={a[1]}\n                  y2={b[1]}\n                  strokeDasharray={strokeDasharray}\n                />\n              ))}\n        </pattern>\n      </defs>\n\n      <rect width=\"100%\" height=\"100%\" fill={`url(#${id})`} stroke=\"none\" />\n\n      {hexagons && hexagons.length > 0 && (\n        <svg aria-hidden=\"true\" className=\"overflow-visible\" x={x} y={y}>\n          {hexagons.map(([col, row]) => {\n            const [cx, cy] = hexCenter(col, row, radius, direction, gap)\n            return (\n              <polygon\n                key={`${col}-${row}`}\n                points={hexPoints(cx, cy, radius - 1, direction)}\n                strokeWidth=\"0\"\n              />\n            )\n          })}\n        </svg>\n      )}\n    </svg>\n  )\n}\n",
      "type": "registry:ui"
    }
  ]
}