Marquee
Loops logos, testimonials or tickers without end in any of four directions on a plain CSS keyframe animation, with adjustable speed and gap, a soft fade mask at both ends and an optional pause while the pointer rests on it.
Preview
Install
Add it with the shadcn CLI, or copy the source manually.
$ bunx --bun shadcn add @karaui/marquee
Needs the theme tokens once. Already ran
shadcn init? You are set. Theme setupInstall dependencies
npm i clsx tailwind-mergeAdd util file
TSXlib/utils.ts
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
Copy the source code
TSXcomponents/motion/marquee.tsx
// karaui.kamisama-v0.my.id/components/motion/marquee
import { Children, type ReactNode } from "react";
import { cn } from "@/lib/utils";
export interface MarqueeProps {
children: ReactNode;
direction?: "left" | "right" | "up" | "down";
speed?: number;
pauseOnHover?: boolean;
gap?: string;
className?: string;
fade?: boolean;
}
export function Marquee({
children,
direction = "left",
speed = 30,
pauseOnHover = true,
gap = "1rem",
className,
fade = true,
}: MarqueeProps) {
const vertical = direction === "up" || direction === "down";
const reverse = direction === "right" || direction === "down";
const items = Children.toArray(children);
return (
<div
className={cn(
"group relative flex overflow-hidden",
vertical ? "flex-col" : "flex-row",
fade && !vertical && "[mask-image:linear-gradient(to_right,transparent,black_12%,black_88%,transparent)]",
fade && vertical && "[mask-image:linear-gradient(to_bottom,transparent,black_12%,black_88%,transparent)]",
className,
)}
// gap on the wrapper too, so the seam between the two tracks matches the
// spacing between items and the loop stays even.
style={{ "--gap": gap, gap } as React.CSSProperties}
>
{[0, 1].map((dup) => (
<div
key={dup}
aria-hidden={dup === 1}
inert={dup === 1}
style={{
animationDuration: `${speed}s`,
animationDirection: reverse ? "reverse" : "normal",
gap,
}}
className={cn(
"flex shrink-0 items-center",
vertical ? "flex-col animate-marquee-vertical" : "flex-row animate-marquee",
pauseOnHover && "group-hover:[animation-play-state:paused]",
)}
>
{items.map((child, i) => (
// biome-ignore lint/suspicious/noArrayIndexKey: Marquee duplicates static child slots; item order is not mutated.
<div key={i} className="shrink-0">
{child}
</div>
))}
</div>
))}
</div>
);
}
@karaui not found? Add it to components.json
{
"registries": {
"@karaui": "https://karaui.kamisama-v0.my.id/r/{name}.json"
}
}@karaui is in shadcn's public registry directory, so the latest CLI needs no setup. On an older CLI, merge this into the existing file once and every @karaui/… install resolves.
API Reference
direction?"left" | "right" | "up" | "down"leftspeed?number30pauseOnHover?booleantruegap?string1remclassName?string—fade?booleantrueUpdated