{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "glyph",
  "title": "Glyph",
  "description": "The Twitter/X-style verified checkmark, with the check cut out of the rosette as a real hole via an SVG mask so whatever sits behind it shows through.",
  "registryDependencies": [
    "https://www.deltacomponents.dev/r/utils.json"
  ],
  "files": [
    {
      "path": "registry/ui/glyph.tsx",
      "content": "\"use client\";\n\nimport {\n  cloneElement,\n  forwardRef,\n  isValidElement,\n  useId,\n  type ReactElement,\n  type ReactNode,\n  type SVGAttributes,\n} from \"react\";\nimport { cn } from \"@/lib/utils\";\n\n// Shape outlines, one path each in a 24x24 viewBox. rosette/shield/hexagon\n// reuse lucide's badge-check/shield/hexagon coordinates (ISC license), not a\n// dependency — only the coordinates are reused. circle/squircle/seal/ribbon\n// are hand-built. All seven were tuned and verified against real icon\n// knockouts in the glyph lab before landing here.\nconst MASKS = {\n  rosette:\n    \"M3.85 8.62a4 4 0 0 1 4.78-4.77 4 4 0 0 1 6.74 0 4 4 0 0 1 4.78 4.78 4 4 0 0 1 0 6.74 4 4 0 0 1-4.77 4.78 4 4 0 0 1-6.75 0 4 4 0 0 1-4.78-4.77 4 4 0 0 1 0-6.76Z\",\n  circle: \"M22 12A10 10 0 1 1 2 12A10 10 0 1 1 22 12Z\",\n  squircle:\n    \"M10 2H14A8 8 0 0 1 22 10V14A8 8 0 0 1 14 22H10A8 8 0 0 1 2 14V10A8 8 0 0 1 10 2Z\",\n  shield:\n    \"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z\",\n  hexagon:\n    \"M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z\",\n  seal: \"M12.00 1.70 L14.91 4.98 L19.28 4.72 L19.02 9.09 L22.30 12.00 L19.02 14.91 L19.28 19.28 L14.91 19.02 L12.00 22.30 L9.09 19.02 L4.72 19.28 L4.98 14.91 L1.70 12.00 L4.98 9.09 L4.72 4.72 L9.09 4.98 Z\",\n  ribbon:\n    \"M18.5 8.5a6.5 6.5 0 1 1-13 0 6.5 6.5 0 0 1 13 0ZM8 13L11 13L7 22ZM13 13L16 13L17 22Z\",\n} as const;\n\nexport type GlyphMask = keyof typeof MASKS;\n\nexport interface GlyphProps extends SVGAttributes<SVGSVGElement> {\n  /** Shape to cut the icon out of. */\n  mask?: GlyphMask;\n  /** The icon to knock out of the shape — any icon element (lucide,\n   *  phosphor, a hand-rolled `<svg>`) that colors itself via `currentColor`. */\n  children: ReactNode;\n  /**\n   * Scale of the icon relative to the shape's 24x24 box, applied about the\n   * center. Icon libraries draw a path that fills its own viewBox, so at\n   * `scale={1}` the icon swallows the shape instead of reading as a mark cut\n   * out of it (a star eats a circle whole, a crown notches a squircle). 0.55\n   * is where a typical full-bleed icon (lucide's `Check`, `Flame`, `Zap`)\n   * reads as a hole rather than a second shape; tune per icon if it's\n   * unusually thin, bold, or already small within its own box.\n   */\n  scale?: number;\n  /** Accessible label. Omit to keep the badge decorative (aria-hidden) when\n   *  it sits beside text that already announces \"verified\". */\n  label?: string;\n  className?: string;\n}\n\n/**\n * A shape (`mask`) with an arbitrary icon (`children`) cut out of it as a\n * genuine hole via an SVG mask, so whatever sits behind the badge (a photo,\n * a gradient) shows through instead of a painted icon seaming against it.\n * `className` both paints and sizes the shape: it fills with `currentColor`,\n * so a `text-*` utility colors it, and defaults to `size-5`, so `size-*` (or\n * `h-*`/`w-*`) resizes it the same way it would a lucide icon.\n */\nexport const Glyph = forwardRef<SVGSVGElement, GlyphProps>(\n  (\n    { mask = \"rosette\", children, scale = 0.55, label, className, ...props },\n    ref\n  ) => {\n    // useId() can return characters like \":\" (e.g. \":r0:\"), which isn't a\n    // legal SVG id in every engine.\n    const maskId = `glyph-${useId().replace(/:/g, \"\")}`;\n    const shapePath = MASKS[mask];\n\n    // Icon components render their own <svg width height> — left alone,\n    // those pixel dimensions win over the scale transform below in some\n    // browsers instead of being scaled by it. Forcing them back to the\n    // icon's native 24x24 box neutralises that without touching whatever\n    // size the consumer's own element requested.\n    const icon = isValidElement<SVGAttributes<SVGSVGElement>>(children)\n      ? cloneElement(children as ReactElement<SVGAttributes<SVGSVGElement>>, {\n          width: 24,\n          height: 24,\n          // Divided back out of the scale below. SVG scales stroke-width along\n          // with the transform, so a stroked icon shrunk to fit also thins out\n          // — at the 0.55 default a lucide icon's 2 lands at 1.1 and the mark\n          // reads spidery rather than cut. Dividing keeps the rendered weight\n          // constant however far the icon is inset. Harmless on a fill-based\n          // icon, which ignores it.\n          strokeWidth:\n            (Number(\n              (children as ReactElement<SVGAttributes<SVGSVGElement>>).props\n                .strokeWidth\n            ) || 2) / scale,\n        })\n      : children;\n\n    return (\n      <svg\n        ref={ref}\n        viewBox=\"0 0 24 24\"\n        // Sizing is a class, not a prop, so Glyph behaves like any other icon.\n        // The two spellings win differently: `size-4` displaces the default\n        // through tailwind-merge, while `h-4 w-4` does not — tailwind-merge\n        // treats those as a different group and leaves both classes on the\n        // element, so it wins only because Tailwind emits `h-*`/`w-*` after\n        // `size-*`. Both were measured; keep the default a `size-*` class so\n        // that stays true.\n        className={cn(\"size-5\", className)}\n        {...(label\n          ? { role: \"img\" }\n          : { \"aria-hidden\": true, role: undefined })}\n        {...props}\n      >\n        {label && <title>{label}</title>}\n        <mask id={maskId}>\n          <path d={shapePath} fill=\"white\" />\n          {/* color: black makes any currentColor-based stroke or fill in the\n              icon resolve to black, which is what becomes the hole — this is\n              what lets both a stroke-based icon (lucide) and a fill-based one\n              (phosphor) knock out without the caller choosing a mode.\n              Scaling happens about the 24x24 center so the icon shrinks in\n              place instead of drifting toward a corner. */}\n          <g\n            style={{ color: \"black\" }}\n            transform={`translate(12 12) scale(${scale}) translate(-12 -12)`}\n          >\n            {icon}\n          </g>\n        </mask>\n        <path d={shapePath} fill=\"currentColor\" mask={`url(#${maskId})`} />\n      </svg>\n    );\n  }\n);\n\nGlyph.displayName = \"Glyph\";\n",
      "type": "registry:ui",
      "target": "components/ui/glyph.tsx"
    }
  ],
  "docs": "Docs & live playground: https://www.deltacomponents.dev/docs/glyph.",
  "categories": [
    "components"
  ],
  "type": "registry:ui"
}
