{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "logo-cloud-carousel",
  "type": "registry:ui",
  "title": "Logo Cloud Carousel",
  "description": "A Logo Cloud Carousel component for displaying a carousel of logos.",
  "files": [
    {
      "path": "src/registry/ui/logo-cloud-carousel.tsx",
      "type": "registry:ui",
      "content": "import { cn } from \"@/lib/utils\";\n\nexport type LogoCloudLogo = {\n  /** Used as the image alt text. */\n  name: string;\n  src: string;\n  /** Shown instead of `src` when a `.dark` ancestor is present. */\n  darkSrc?: string;\n  href?: string;\n};\n\n/** @deprecated Use `src` / `darkSrc`. Kept so older call sites keep compiling. */\ntype LegacyLogo = { name: string; url: string; darkUrl?: string; href?: string };\n\nexport type LogoCloudCarouselProps = React.ComponentProps<\"div\"> & {\n  logos: (LogoCloudLogo | LegacyLogo)[];\n  /** Seconds for one full loop. @default 40 */\n  speed?: number;\n  /** Scroll direction of the first row. @default \"left\" */\n  direction?: \"left\" | \"right\";\n  /** A second row scrolls in the opposite direction. @default 1 */\n  rows?: 1 | 2;\n  /** Pause while hovered. @default true */\n  pauseOnHover?: boolean;\n  /** Fade the left and right edges. @default true */\n  fade?: boolean;\n};\n\n// Must match the keyframe name used in the animate-[...] class below.\nconst KEYFRAMES = \"logo-cloud-marquee\";\n\nfunction normalize(logo: LogoCloudLogo | LegacyLogo): LogoCloudLogo {\n  if (\"src\" in logo) return logo;\n  return { name: logo.name, src: logo.url, darkSrc: logo.darkUrl, href: logo.href };\n}\n\n/**\n * Infinite logo marquee in pure CSS. With reduced motion it becomes a static, wrapped row.\n * Use `className` for spacing; the component adds no outer padding.\n */\nexport function LogoCloudCarousel({\n  logos,\n  speed = 40,\n  direction = \"left\",\n  rows = 1,\n  pauseOnHover = true,\n  fade = true,\n  className,\n  style,\n  ...props\n}: LogoCloudCarouselProps) {\n  const items = logos.map(normalize);\n  const rowDirections = rows === 2 ? [direction, direction === \"left\" ? \"right\" : \"left\"] : [direction];\n\n  return (\n    <div\n      data-slot=\"logo-cloud-carousel\"\n      className={cn(\n        \"group/logo-cloud flex flex-col gap-6 overflow-hidden\",\n        fade &&\n          \"[mask-image:linear-gradient(to_right,transparent,black_10%,black_90%,transparent)] motion-reduce:[mask-image:none]\",\n        className\n      )}\n      style={{ \"--logo-cloud-speed\": `${speed}s`, ...style } as React.CSSProperties}\n      {...props}\n    >\n      <style href={KEYFRAMES} precedence=\"default\">\n        {`@keyframes ${KEYFRAMES}{from{transform:translateX(0)}to{transform:translateX(-50%)}}`}\n      </style>\n      {rowDirections.map((rowDirection, row) => (\n        <div\n          key={row}\n          className={cn(\n            \"flex w-max\",\n            rowDirection === \"left\"\n              ? \"animate-[logo-cloud-marquee_var(--logo-cloud-speed)_linear_infinite]\"\n              : \"animate-[logo-cloud-marquee_var(--logo-cloud-speed)_linear_infinite_reverse]\",\n            pauseOnHover && \"group-hover/logo-cloud:[animation-play-state:paused]\",\n            \"motion-reduce:w-full motion-reduce:animate-none\",\n            row > 0 && \"motion-reduce:hidden\"\n          )}\n          aria-hidden={row > 0 || undefined}\n        >\n          <LogoList logos={items} hidden={row > 0} />\n          <LogoList logos={items} hidden className=\"motion-reduce:hidden\" />\n        </div>\n      ))}\n    </div>\n  );\n}\n\nfunction LogoList({\n  logos,\n  hidden,\n  className,\n}: {\n  logos: LogoCloudLogo[];\n  hidden?: boolean;\n  className?: string;\n}) {\n  return (\n    <ul\n      aria-hidden={hidden || undefined}\n      className={cn(\n        // min-w-full keeps the loop seamless when there are only a few logos.\n        \"flex min-w-full shrink-0 items-center justify-around gap-12 pr-12\",\n        \"motion-reduce:flex-wrap motion-reduce:justify-center motion-reduce:gap-y-6 motion-reduce:pr-0\",\n        className\n      )}\n    >\n      {logos.map((logo) => (\n        <li key={logo.name} className=\"shrink-0\">\n          {logo.href ? (\n            <a\n              href={logo.href}\n              tabIndex={hidden ? -1 : undefined}\n              className=\"block rounded-md opacity-70 outline-none transition-opacity hover:opacity-100 focus-visible:opacity-100 focus-visible:ring-2 focus-visible:ring-ring\"\n            >\n              <LogoImage logo={logo} />\n            </a>\n          ) : (\n            <div className=\"opacity-70\">\n              <LogoImage logo={logo} />\n            </div>\n          )}\n        </li>\n      ))}\n    </ul>\n  );\n}\n\n// Plain <img> keeps the component framework-agnostic and works with SVG logos of unknown size.\nfunction LogoImage({ logo }: { logo: LogoCloudLogo }) {\n  const imgClass = \"h-8 w-auto max-w-40 object-contain\";\n  return (\n    <>\n      {/* eslint-disable-next-line @next/next/no-img-element */}\n      <img\n        src={logo.src}\n        alt={logo.name}\n        height={32}\n        loading=\"lazy\"\n        decoding=\"async\"\n        className={cn(imgClass, logo.darkSrc && \"dark:hidden\")}\n      />\n      {logo.darkSrc && (\n        // eslint-disable-next-line @next/next/no-img-element\n        <img\n          src={logo.darkSrc}\n          alt={logo.name}\n          height={32}\n          loading=\"lazy\"\n          decoding=\"async\"\n          className={cn(imgClass, \"hidden dark:block\")}\n        />\n      )}\n    </>\n  );\n}\n"
    }
  ]
}
