{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "tech-stack",
  "title": "Tech stack logos",
  "description": "A tilted, overlapping stack of technology logos that fans out on hover or focus and shows each logo’s label in a tooltip.",
  "dependencies": [
    "motion",
    "radix-ui"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/items/tech-stack/tech-stack.tsx",
      "content": "/* eslint-disable @next/next/no-img-element -- Consumer-provided logos keep this registry component framework independent. */\n\"use client\";\n\nimport { useRef, useState, type CSSProperties } from \"react\";\nimport { useInView, useReducedMotion, useReducedMotionConfig } from \"motion/react\";\nimport { Tooltip } from \"radix-ui\";\nimport { cn } from \"@/lib/utils\";\n\nexport type TechStackItem = {\n  src: string;\n  label: string;\n  subtitle?: string;\n  rotate?: number;\n  contain?: boolean;\n  /** Token-based classes such as bg-card or bg-ink. */\n  className?: string;\n};\n\nexport function TechStack({ items, size = 40, overlap = 8, expandedGap = 8, expandOnView = true, className }: {\n  items: TechStackItem[];\n  size?: number;\n  overlap?: number;\n  expandedGap?: number;\n  expandOnView?: boolean;\n  className?: string;\n}) {\n  const ref = useRef<HTMLDivElement>(null);\n  const inView = useInView(ref, { margin: \"-20% 0px -35% 0px\" });\n  const preference = useReducedMotion();\n  const configured = useReducedMotionConfig();\n  const reduced = preference || configured;\n  const [open, setOpen] = useState<string | null>(null);\n  return (\n    <Tooltip.Provider delayDuration={120}>\n      <div ref={ref} role=\"group\" aria-label=\"Built with\" data-inview={expandOnView && inView} className={cn(\"group/stack flex max-w-full flex-wrap items-center gap-y-4 py-3 font-body\", className)}\n        style={{ width: items.length * size + Math.max(0, items.length - 1) * expandedGap, \"--stack-overlap\": `${-Math.max(0, overlap)}px`, \"--stack-gap\": `${expandedGap}px` } as CSSProperties}>\n        {items.map((item, index) => (\n          <Tooltip.Root key={item.label} open={open === item.label} onOpenChange={(next) => setOpen(next ? item.label : null)}>\n            <Tooltip.Trigger {...{ asChild: true }}>\n              <button type=\"button\" aria-label={item.label} style={{ \"--tile-angle\": reduced ? \"0deg\" : `${item.rotate ?? (index % 2 ? 4 : -5)}deg`, zIndex: items.length - index } as CSSProperties}\n                onClick={(event) => { event.preventDefault(); setOpen(item.label); }}\n                className=\"relative flex shrink-0 flex-col items-center gap-1 rounded-inset text-ink transition-[margin,transform] duration-300 not-first:ml-(--stack-overlap) rotate-(--tile-angle) group-hover/stack:rotate-0 group-hover/stack:not-first:ml-(--stack-gap) group-focus-within/stack:rotate-0 group-focus-within/stack:not-first:ml-(--stack-gap) max-lg:group-data-[inview=true]/stack:rotate-0 max-lg:group-data-[inview=true]/stack:not-first:ml-(--stack-gap) focus-visible:z-50 focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-ring motion-reduce:rotate-0 motion-reduce:transition-none\">\n                <img src={item.src} alt=\"\" width={size} height={size} className={cn(\"block rounded-inset bg-card shadow-md\", item.contain ? \"object-contain p-0.5\" : \"object-cover\", item.className)} style={{ width: size, height: size }} />\n              </button>\n            </Tooltip.Trigger>\n            <Tooltip.Portal>\n              <Tooltip.Content sideOffset={8} collisionPadding={12} className=\"z-50 rounded-inset border border-rule bg-ink px-3 py-2 font-body text-[12px] text-on-solid shadow-lg\">\n                <span className=\"block font-medium\">{item.label}</span>\n                {item.subtitle ? <span className=\"block text-[11px] text-on-solid/70\">{item.subtitle}</span> : null}\n              </Tooltip.Content>\n            </Tooltip.Portal>\n          </Tooltip.Root>\n        ))}\n      </div>\n    </Tooltip.Provider>\n  );\n}\n",
      "type": "registry:component"
    }
  ],
  "meta": {
    "kind": "widget",
    "whenToUse": "Use beside project descriptions to list technologies. Logos and text are props; hover, keyboard focus and touch reveal the details.",
    "origin": {
      "project": "Portfolio",
      "path": "src/components/project-tech-stack.tsx"
    },
    "credits": [
      {
        "name": "Technology brand logos from Wikimedia Commons and Icons8, preview fixtures only (per-file licences not recorded; Icons8's free licence requires a link back)",
        "license": "unknown"
      }
    ]
  },
  "type": "registry:component"
}
