{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "marquee",
  "type": "registry:ui",
  "title": "Marquee",
  "description": "Infinite-scrolling row of content (logos, testimonials) — pure CSS, no animation frames.",
  "dependencies": [
    "@saasflare/ui"
  ],
  "files": [
    {
      "path": "components/ui/marquee.tsx",
      "type": "registry:ui",
      "content": "\"use client\"\n\n/**\n * @fileoverview Infinite scrolling marquee for logos, testimonials, or any content.\n * @author Saasflare™\n * CSS-only animation for performance — no JavaScript animation frames.\n * Duplicates children to create seamless infinite scroll illusion.\n * @module packages/ui/components/ui/marquee\n * @package ui\n *\n * @component\n * @example\n * import { Marquee } from '@saasflare/ui';\n * <Marquee>\n *   <img src=\"/logo1.svg\" alt=\"Logo 1\" />\n *   <img src=\"/logo2.svg\" alt=\"Logo 2\" />\n *   <img src=\"/logo3.svg\" alt=\"Logo 3\" />\n * </Marquee>\n *\n * @example\n * // Reverse direction, slower speed\n * <Marquee reverse speed={60} pauseOnHover>\n *   {logos.map(logo => <LogoCard key={logo.id} {...logo} />)}\n * </Marquee>\n */\n\nimport * as React from \"react\"\nimport { type ReactNode } from \"react\"\nimport { cn } from \"@saasflare/ui\"\nimport { useSaasflareProps, type SaasflareComponentProps } from \"@saasflare/ui\"\nimport { useReducedMotion } from \"@saasflare/ui\"\n\n/** Props for the Marquee component. */\nexport interface MarqueeProps\n  extends Omit<React.ComponentProps<\"div\">, keyof SaasflareComponentProps | \"children\">,\n    SaasflareComponentProps {\n  /** Items to scroll infinitely. */\n  children: ReactNode\n  /** Reverse scroll direction. Default: `false` */\n  reverse?: boolean\n  /** Scroll duration in seconds for one full cycle. Default: `40` */\n  speed?: number\n  /** Pause scrolling on hover. Default: `true` */\n  pauseOnHover?: boolean\n  /** Gap between items in pixels. Default: `48` */\n  gap?: number\n  /** Number of times to duplicate the content strip. Default: `2` */\n  repeat?: number\n  /** Enable vertical scrolling instead of horizontal. Default: `false` */\n  vertical?: boolean\n  /** Additional class names for the container. */\n  className?: string\n}\n\n/**\n * Infinite-scrolling content ticker.\n *\n * - Pure CSS animation (no JS frames, no Motion overhead)\n * - Duplicates children to create a seamless loop\n * - Pauses on hover for readability (configurable)\n * - Falls back to static flex row when reduced motion is preferred\n *\n * @component\n * @package ui\n */\nexport function Marquee({\n  children,\n  reverse = false,\n  speed = 40,\n  pauseOnHover = true,\n  gap = 48,\n  repeat = 2,\n  vertical = false,\n  className,\n  surface,\n  radius,\n  animated,\n  iconWeight,\n  ...props\n}: MarqueeProps) {\n  const sf = useSaasflareProps({ surface, radius, animated, iconWeight })\n  const reduced = useReducedMotion()\n  // Scope keyframe names to this instance so multiple mounted Marquees never\n  // collide on global @keyframes identifiers (also stable across SSR/CSR).\n  const rawId = React.useId()\n  const uid = rawId.replace(/[^a-zA-Z0-9_-]/g, \"\")\n\n  // Honor BOTH the provider-level `animated` axis and the user's reduced-motion\n  // preference. A consumer who set animated=false must get a static fallback,\n  // not a moving marquee.\n  if (reduced || !sf.animated) {\n    return (\n      <div\n        {...props}\n        className={cn(\n          \"flex items-center overflow-hidden\",\n          vertical ? \"flex-col\" : \"flex-row\",\n          className,\n        )}\n        style={{ gap }}\n        data-slot=\"marquee\"\n        data-surface={sf.surface}\n        data-radius={sf.radius}\n        data-animated={String(sf.animated)}\n      >\n        {children}\n      </div>\n    )\n  }\n\n  const animationName = vertical\n    ? `marquee-vertical-${uid}`\n    : `marquee-horizontal-${uid}`\n  const direction = reverse ? \"reverse\" : \"normal\"\n\n  return (\n    <div\n      {...props}\n      className={cn(\n        \"group relative flex overflow-hidden\",\n        vertical ? \"flex-col\" : \"flex-row\",\n        className,\n      )}\n      data-slot=\"marquee\"\n      data-surface={sf.surface}\n      data-radius={sf.radius}\n      data-animated={String(sf.animated)}\n    >\n      <style>{`\n        @keyframes marquee-horizontal-${uid} {\n          from { transform: translateX(0); }\n          to { transform: translateX(-100%); }\n        }\n        @keyframes marquee-vertical-${uid} {\n          from { transform: translateY(0); }\n          to { transform: translateY(-100%); }\n        }\n      `}</style>\n\n      {Array.from({ length: repeat }, (_, i) => (\n        <div\n          key={i}\n          className={cn(\n            \"flex shrink-0 items-center\",\n            vertical ? \"flex-col\" : \"flex-row\",\n            pauseOnHover && \"group-hover:[animation-play-state:paused]\",\n          )}\n          style={{\n            gap,\n            animation: `${animationName} ${speed}s linear infinite`,\n            animationDirection: direction,\n          }}\n          aria-hidden={i > 0}\n        >\n          {children}\n        </div>\n      ))}\n    </div>\n  )\n}\n",
      "target": "components/ui/marquee.tsx"
    }
  ],
  "$meta": {
    "source": "https://ui.saasflare.io/r/marquee.json"
  }
}
