{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "marquee",
  "title": "Marquee",
  "description": "A row that runs on its own, seamlessly, and turns back into an ordinary scrolling row for anyone who asked for less motion.",
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/default/marquee/marquee.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport type MarqueeProps = React.HTMLAttributes<HTMLDivElement> & {\n  /** Which way the content travels. */\n  direction?: \"left\" | \"right\" | \"up\" | \"down\"\n  /** Seconds for one full pass. Longer content wants longer. */\n  duration?: number\n  /** Pixels between the items, and between one repeat and the next. */\n  gap?: number\n  /** Holds still while the pointer is over it. */\n  pauseOnHover?: boolean\n  /** How many times the children repeat. Two is enough to hide the seam. */\n  copies?: number\n  /** Softens both ends, so items arrive and leave rather than pop. */\n  fade?: boolean\n}\n\n/**\n * A row that runs on its own. The children are repeated so the loop has no\n * visible seam, and every repeat after the first is hidden from assistive\n * technology rather than read out again.\n *\n * With reduced motion it stops being a marquee: the repeats go, the animation\n * goes, and what is left scrolls, so the content is all still reachable rather\n * than being moved past somebody who asked for none of it.\n */\nexport function Marquee({\n  direction = \"left\",\n  duration = 20,\n  gap = 16,\n  pauseOnHover = false,\n  copies = 2,\n  fade = false,\n  className,\n  style,\n  children,\n  ...rootProps\n}: MarqueeProps) {\n  const vertical = direction === \"up\" || direction === \"down\"\n  const reversed = direction === \"right\" || direction === \"down\"\n\n  // Two copies is the least that can hide the seam; one would tear.\n  const repeats = Math.max(2, Math.trunc(copies))\n\n  // Each copy carries its own trailing gap, so every copy occupies exactly the\n  // same length. That is what makes one copy's worth of travel seamless.\n  const shift = `-${100 / repeats}%`\n  const edge = vertical ? \"to bottom\" : \"to right\"\n\n  return (\n    <div\n      data-slot=\"marquee\"\n      data-direction={direction}\n      className={cn(\n        \"group/marquee relative flex overflow-hidden\",\n        vertical\n          ? \"flex-col motion-reduce:overflow-y-auto\"\n          : \"flex-row motion-reduce:overflow-x-auto\",\n        className\n      )}\n      style={{\n        ...(fade\n          ? {\n              maskImage: `linear-gradient(${edge}, transparent, black 8%, black 92%, transparent)`,\n            }\n          : null),\n        ...style,\n      }}\n      {...rootProps}\n    >\n      <div\n        className={cn(\n          \"flex shrink-0\",\n          vertical ? \"h-max flex-col\" : \"w-max flex-row\",\n          vertical\n            ? \"motion-safe:animate-[mischief-marquee-y_var(--marquee-duration)_linear_infinite]\"\n            : \"motion-safe:animate-[mischief-marquee-x_var(--marquee-duration)_linear_infinite]\",\n          reversed && \"[animation-direction:reverse]\",\n          pauseOnHover && \"group-hover/marquee:[animation-play-state:paused]\"\n        )}\n        style={\n          {\n            \"--marquee-duration\": `${duration}s`,\n            \"--marquee-shift\": shift,\n          } as React.CSSProperties\n        }\n      >\n        {Array.from({ length: repeats }, (_, copy) => (\n          <div\n            key={copy}\n            // The first copy is the content. The rest exist to hide the seam,\n            // and are not content a screen reader should meet again.\n            aria-hidden={copy > 0 ? \"true\" : undefined}\n            className={cn(\n              \"flex shrink-0 items-center\",\n              vertical ? \"flex-col\" : \"flex-row\",\n              copy > 0 && \"motion-reduce:hidden\"\n            )}\n            style={{\n              gap: `${gap}px`,\n              ...(vertical\n                ? { paddingBottom: `${gap}px` }\n                : { paddingInlineEnd: `${gap}px` }),\n            }}\n          >\n            {children}\n          </div>\n        ))}\n      </div>\n    </div>\n  )\n}\n",
      "type": "registry:ui"
    }
  ],
  "css": {
    "@keyframes mischief-marquee-x": {
      "to": {
        "transform": "translateX(var(--marquee-shift))"
      }
    },
    "@keyframes mischief-marquee-y": {
      "to": {
        "transform": "translateY(var(--marquee-shift))"
      }
    }
  },
  "categories": [
    "motion",
    "scroll",
    "marquee",
    "layout"
  ],
  "type": "registry:ui"
}