{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "rubber-stamp",
  "type": "registry:ui",
  "title": "Rubber Stamp",
  "description": "A speckled ink stamp that thuds onto the page.",
  "files": [
    {
      "path": "ui/rubber-stamp.tsx",
      "type": "registry:ui",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nconst INKS = {\n  red: \"#c0262d\",\n  blue: \"#1f4fa3\",\n  green: \"#2d7a3e\",\n  black: \"#26241f\",\n  purple: \"#6b2fa0\",\n} as const;\n\nexport interface RubberStampProps extends React.HTMLAttributes<HTMLDivElement> {\n  children: React.ReactNode;\n  /** Small line under the main text, e.g. a date */\n  sub?: React.ReactNode;\n  ink?: keyof typeof INKS | (string & {});\n  rotate?: number;\n  shape?: \"box\" | \"round\";\n  /** Thud onto the page when scrolled into view. */\n  animate?: boolean;\n}\n\n// speckled ink: noise thresholded into an alpha mask\nconst GRUNGE =\n  \"url(\\\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='300' height='300'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='.7' numOctaves='3' seed='4' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0  0 0 0 0 0  0 0 0 0 0  -4 0 0 0 3.2'/></filter><rect width='100%' height='100%' filter='url(%23g)'/></svg>\\\") 0 0 / 300px 300px\";\n\nconst CSS = `\n@keyframes eph-thud {\n  0% { opacity: 0; transform: rotate(var(--eph-r)) scale(2.2); }\n  55% { opacity: 1; transform: rotate(var(--eph-r)) scale(.94); }\n  72% { transform: rotate(var(--eph-r)) scale(1.03); }\n  100% { opacity: 1; transform: rotate(var(--eph-r)) scale(1); }\n}\n.eph-stamp-wait { opacity: 0; }\n@media (prefers-reduced-motion: reduce) {\n  .eph-stamp-wait { opacity: 1; }\n  .eph-stamp { animation: none !important; }\n}\n`;\n\nfunction useInView<T extends Element>() {\n  const ref = React.useRef<T>(null);\n  const [inView, setInView] = React.useState(false);\n  React.useEffect(() => {\n    const el = ref.current;\n    if (!el) return;\n    if (typeof IntersectionObserver === \"undefined\") return setInView(true);\n    const io = new IntersectionObserver(\n      ([entry]) => {\n        if (entry.isIntersecting) {\n          setInView(true);\n          io.disconnect();\n        }\n      },\n      { threshold: 0.6 },\n    );\n    io.observe(el);\n    return () => io.disconnect();\n  }, []);\n  return [ref, inView] as const;\n}\n\nexport function RubberStamp({\n  children,\n  sub,\n  ink = \"red\",\n  rotate = -8,\n  shape = \"box\",\n  animate = true,\n  className = \"\",\n  style,\n  ...props\n}: RubberStampProps) {\n  const [ref, inView] = useInView<HTMLDivElement>();\n  const color = INKS[ink as keyof typeof INKS] ?? ink;\n  const round = shape === \"round\";\n\n  return (\n    <div\n      ref={ref}\n      className={`eph-stamp inline-flex select-none items-center justify-center ${animate && !inView ? \"eph-stamp-wait\" : \"\"} ${className}`}\n      style={\n        {\n          \"--eph-r\": `${rotate}deg`,\n          transform: `rotate(${rotate}deg)`,\n          color,\n          mixBlendMode: \"multiply\",\n          mask: GRUNGE,\n          WebkitMask: GRUNGE,\n          animation: animate && inView ? \"eph-thud .45s cubic-bezier(.2,.7,.3,1) both\" : undefined,\n          ...style,\n        } as React.CSSProperties\n      }\n      {...props}\n    >\n      <style href=\"eph-rubber-stamp\" precedence=\"default\">\n        {CSS}\n      </style>\n      <div\n        className={`flex flex-col items-center justify-center ${round ? \"rounded-full p-[5px]\" : \"rounded-[6px] p-[4px]\"}`}\n        style={{ border: \"4px solid currentColor\" }}\n      >\n        <div\n          className={`flex flex-col items-center justify-center ${round ? \"aspect-square rounded-full px-5\" : \"rounded-[3px] px-4 py-1.5\"}`}\n          style={{ border: \"1.5px solid currentColor\" }}\n        >\n          <span\n            className=\"whitespace-nowrap text-[30px] font-black uppercase leading-none tracking-[0.08em]\"\n            style={{ fontFamily: \"var(--font-stamp, Impact, 'Arial Narrow Bold', sans-serif)\" }}\n          >\n            {children}\n          </span>\n          {sub && (\n            <span\n              className=\"mt-1 whitespace-nowrap text-[11px] font-bold uppercase tracking-[0.25em]\"\n              style={{ fontFamily: \"var(--font-type, ui-monospace, monospace)\" }}\n            >\n              {sub}\n            </span>\n          )}\n        </div>\n      </div>\n    </div>\n  );\n}\n"
    }
  ],
  "categories": [
    "extras"
  ]
}