Design
llms.txt digitalcrew.tech

Motion

InfiniteSlider

A JavaScript-driven endless strip (Framer Motion) that renders its children twice and slides them in a loop, optionally slowing on hover.

Crew OSMotionDigitalCrew.InfiniteSlider
InfiniteSlider · liveOpen

Guidelines#

Use it for customer and partner logo rows under a hero. Pair it with ProgressiveBlur and background fades at both ends, as the Crew OS hero does.

What you provide: children (one set of logos), gap (px, default 16), duration (s per loop, default 25), durationOnHover (s, slows or speeds the loop while hovered), direction (horizontal default, vertical), reverse (default false), className. The hero uses duration={40}, durationOnHover={80}, gap={112} with 40px-tall logos (dark:invert), 80px bg-linear-to-r from-background fades and ProgressiveBlur (blurIntensity={1}) on each edge.

Anatomy: an overflow-hidden wrapper around a flex w-max track with the gap applied inline.

Behaviour: the track is measured (react-use-measure) and translated linearly by half its length plus half a gap per loop, then snaps back. On hover it eases into the new duration from its current position. It does not check reduced motion.

Don't put interactive elements in the strip, or fewer logos than fill the row (the duplicate copy then shows a gap).

Facts#

GroupMotion
ProductCrew OS
Globalwindow.DigitalCrew.InfiniteSlider
Sourcedigitalcrew-orchestrator: components/ui/infinite-slider.tsx
Import in the appimport { InfiniteSlider } from "@/components/ui/infinite-slider";

API#

TypeScript declarations emitted from components/ui/infinite-slider.tsx.

type InfiniteSliderProps = {
    children: React.ReactNode;
    gap?: number;
    duration?: number;
    durationOnHover?: number;
    direction?: 'horizontal' | 'vertical';
    reverse?: boolean;
    className?: string;
};
export declare function InfiniteSlider({ children, gap, duration, durationOnHover, direction, reverse, className, }: InfiniteSliderProps): JSX.Element;

Example#

The code of the preview above: plain React.createElement against window.DigitalCrew, with realistic data.

<div id="root" class="p-6"></div>
<script>
(function () {
  var h = React.createElement, DC = window.DigitalCrew, I = DC.Icons;
  var logos = ["Northwind Traders", "Brightline", "Helios Capital", "Atlas Freight", "Vantage Health", "Kestrel Labs"];
  function App() {
    return h("div", { className: "relative py-4" },
      h(DC.InfiniteSlider, { duration: 40, durationOnHover: 80, gap: 64 },
        logos.map(function (n) { return h("div", { key: n, className: "text-muted-foreground flex items-center gap-2 text-lg font-semibold tracking-tight" }, h(I.Building2, { className: "size-5" }), n); })),
      h("div", { className: "absolute inset-y-0 left-0 w-20 bg-linear-to-r from-background" }),
      h("div", { className: "absolute inset-y-0 right-0 w-20 bg-linear-to-l from-background" }),
      h(DC.ProgressiveBlur, { className: "pointer-events-none absolute left-0 top-0 h-full w-20", direction: "left", blurIntensity: 1 }),
      h(DC.ProgressiveBlur, { className: "pointer-events-none absolute right-0 top-0 h-full w-20", direction: "right", blurIntensity: 1 }));
  }
  ReactDOM.createRoot(document.getElementById("root")).render(h(DC.TooltipProvider, { delayDuration: 80 }, h(App)));
})();
</script>