# Marquee Carousel

Children that scroll in an endless loop, with faded edges.

## When to use

* Run a strip of logos, technologies or testimonials across a landing page.
* Show more items than fit in a row without a carousel control.

## How it works

`Marquee` renders its children twice and scrolls them with a CSS animation. Props: `duration` (seconds per loop, default `20`), `direction` (`left`, `right`, `up` or `down`), `pauseOnHover`, `fade` (default `true`) and `fadeAmount` (edge fade in percent, default `10`), plus any `div` attribute. It has no dependencies.

## Example

```tsx
import { Marquee } from "@/proprietary-components/marquee-carousel"

export default function Demo() {
  return (
    <div className="flex items-center justify-center min-h-[400px] w-full">
      <Marquee>
        <span className="mx-8 text-2xl font-medium">React</span>
        <span className="mx-8 text-2xl font-medium">Next.js</span>
        <span className="mx-8 text-2xl font-medium">Tailwind</span>
        <span className="mx-8 text-2xl font-medium">TypeScript</span>
        <span className="mx-8 text-2xl font-medium">Supabase</span>
      </Marquee>
    </div>
  )
}

```

## Code

Copy the component into your project:

```tsx
"use client";

import React from "react";
import { cn } from "@/shadcn/base/lib/utils";

interface MarqueeProps extends React.HTMLAttributes<HTMLDivElement> {
  duration?: number;
  pauseOnHover?: boolean;
  direction?: "left" | "right" | "up" | "down";
  fade?: boolean;
  fadeAmount?: number;
}

export function Marquee({
  children,
  className,
  duration = 20,
  pauseOnHover = false,
  direction = "left",
  fade = true,
  fadeAmount = 10,
  ...props
}: MarqueeProps) {
  const containerRef = React.useRef<HTMLDivElement>(null);
  const [isPaused, setIsPaused] = React.useState(false);

  const items = React.Children.toArray(children);
  const isVertical = direction === "up" || direction === "down";

  return (
    <>
      <style>
        {`
        @keyframes scroll {
          from {
            transform: translateX(0);
          }
          to {
            transform: translateX(-50%);
          }
        }

        @keyframes scroll-reverse {
          from {
            transform: translateX(-50%);
          }
          to {
            transform: translateX(0);
          }
        }

        @keyframes scroll-y {
          from {
            transform: translateY(0);
          }
          to {
            transform: translateY(-50%);
          }
        }

        @keyframes scroll-y-reverse {
          from {
            transform: translateY(-50%);
          }
          to {
            transform: translateY(0);
          }
        }

        .marquee-scroller {
          display: flex;
          animation: ${
          isVertical
            ? (direction === "up" ? "scroll-y" : "scroll-y-reverse")
            : (direction === "left" ? "scroll" : "scroll-reverse")
        } ${duration}s linear infinite;
        }

        .marquee-scroller.paused {
          animation-play-state: paused;
        }
      `}
      </style>
      <div
        ref={containerRef}
        className={cn(
          "flex w-full overflow-hidden",
          isVertical && "flex-col",
          className,
        )}
        style={{
          ...(fade && {
            maskImage: isVertical
              ? `linear-gradient(to bottom, transparent 0%, black ${fadeAmount}%, black ${
                100 - fadeAmount
              }%, transparent 100%)`
              : `linear-gradient(to right, transparent 0%, black ${fadeAmount}%, black ${
                100 - fadeAmount
              }%, transparent 100%)`,
            WebkitMaskImage: isVertical
              ? `linear-gradient(to bottom, transparent 0%, black ${fadeAmount}%, black ${
                100 - fadeAmount
              }%, transparent 100%)`
              : `linear-gradient(to right, transparent 0%, black ${fadeAmount}%, black ${
                100 - fadeAmount
              }%, transparent 100%)`,
          }),
        }}
        onMouseEnter={() => pauseOnHover && setIsPaused(true)}
        onMouseLeave={() => pauseOnHover && setIsPaused(false)}
        {...props}
      >
        <div
          className={cn(
            "marquee-scroller flex shrink-0",
            isVertical && "flex-col",
            isPaused && "paused",
          )}
        >
          {items.map((item, index) => (
            <div
              key={`first-${index}`}
              className={cn("flex shrink-0", isVertical && "w-full")}
            >
              {item}
            </div>
          ))}
          {items.map((item, index) => (
            <div
              key={`second-${index}`}
              className={cn("flex shrink-0", isVertical && "w-full")}
            >
              {item}
            </div>
          ))}
        </div>
      </div>
    </>
  );
}


```
