PreviewCode Installation pnpmnpmyarnbunpnpm add framer-motion lucide-react CopyCopy and paste the following code into your project.components/eldoraui/integrations.tsx"use client"; import { motion, useAnimation, useInView } from "framer-motion"; import { BarChart, File, Globe, HeartHandshake, Rss, Shield, } from "lucide-react"; import { useEffect, useId, useRef, useState } from "react"; import { cn } from "@/lib/utils"; import { Marquee } from "@/components/eldoraui/marquee"; const tiles = [ { icon: <HeartHandshake className="size-full" />, bg: ( <div className="pointer-events-none absolute left-1/2 top-1/2 size-1/2 -translate-x-1/2 -translate-y-1/2 overflow-visible rounded-full bg-gradient-to-r from-orange-600 via-rose-600 to-violet-600 opacity-70 blur-[20px]"></div> ), }, { icon: <Globe className="size-full" />, bg: ( <div className="pointer-events-none absolute left-1/2 top-1/2 size-1/2 -translate-x-1/2 -translate-y-1/2 overflow-visible rounded-full bg-gradient-to-r from-cyan-500 via-blue-500 to-indigo-500 opacity-70 blur-[20px]"></div> ), }, { icon: <File className="size-full" />, bg: ( <div className="pointer-events-none absolute left-1/2 top-1/2 size-1/2 -translate-x-1/2 -translate-y-1/2 overflow-visible rounded-full bg-gradient-to-r from-green-500 via-teal-500 to-emerald-600 opacity-70 blur-[20px]"></div> ), }, { icon: <Shield className="size-full" />, bg: ( <div className="pointer-events-none absolute left-1/2 top-1/2 size-1/2 -translate-x-1/2 -translate-y-1/2 overflow-visible rounded-full bg-gradient-to-r from-yellow-400 via-orange-500 to-yellow-600 opacity-70 blur-[20px]"></div> ), }, { icon: <Rss className="size-full" />, bg: ( <div className="pointer-events-none absolute left-1/2 top-1/2 size-1/2 -translate-x-1/2 -translate-y-1/2 overflow-visible rounded-full bg-gradient-to-r from-orange-600 via-rose-600 to-violet-600 opacity-70 blur-[20px]"></div> ), }, { icon: <BarChart className="size-full" />, bg: ( <div className="pointer-events-none absolute left-1/2 top-1/2 size-1/2 -translate-x-1/2 -translate-y-1/2 overflow-visible rounded-full bg-gradient-to-r from-gray-600 via-gray-500 to-gray-400 opacity-70 blur-[20px]"></div> ), }, ]; function shuffleArray(array: any[]) { let currentIndex = array.length; let randomIndex; // While there remain elements to shuffle. while (currentIndex !== 0) { // Pick a remaining element. randomIndex = Math.floor(Math.random() * currentIndex); currentIndex--; // And swap it with the current element. [array[currentIndex], array[randomIndex]] = [ array[randomIndex], array[currentIndex], ]; } return array; } function Card(card: { icon: JSX.Element; bg: JSX.Element }) { const id = useId(); const controls = useAnimation(); const ref = useRef(null); const inView = useInView(ref, { once: true }); useEffect(() => { if (inView) { controls.start({ opacity: 1, transition: { delay: Math.random() * 2, ease: "easeOut", duration: 1 }, }); } }, [controls, inView]); return ( <motion.div key={id} ref={ref} initial={{ opacity: 0 }} animate={controls} className={cn( "relative size-20 cursor-pointer overflow-hidden rounded-2xl border p-4", // light styles "bg-white [box-shadow:0_0_0_1px_rgba(0,0,0,.03),0_2px_4px_rgba(0,0,0,.05),0_12px_24px_rgba(0,0,0,.05)]", // dark styles "transform-gpu dark:bg-transparent dark:[border:1px_solid_rgba(255,255,255,.1)] dark:[box-shadow:0_-20px_80px_-20px_#ffffff1f_inset]", )} > {card.icon} {card.bg} </motion.div> ); } export function Integrations() { const [randomTiles1, setRandomTiles1] = useState<typeof tiles>([]); const [randomTiles2, setRandomTiles2] = useState<typeof tiles>([]); const [randomTiles3, setRandomTiles3] = useState<typeof tiles>([]); const [randomTiles4, setRandomTiles4] = useState<typeof tiles>([]); useEffect(() => { if (typeof window !== "undefined") { // Ensures this runs client-side setRandomTiles1(shuffleArray([...tiles])); setRandomTiles2(shuffleArray([...tiles])); setRandomTiles3(shuffleArray([...tiles])); setRandomTiles4(shuffleArray([...tiles])); } }, []); return ( <section id="cta"> <div className="container mx-auto px-4 py-12 md:px-8"> <div className="flex w-full flex-col items-center justify-center"> <div className="relative flex w-full flex-col items-center justify-center overflow-hidden"> <Marquee reverse className="-delay-[200ms] [--duration:10s]" repeat={5} > {randomTiles1.map((review, idx) => ( <Card key={idx} {...review} /> ))} </Marquee> <Marquee reverse className="[--duration:25s]" repeat={5}> {randomTiles2.map((review, idx) => ( <Card key={idx} {...review} /> ))} </Marquee> <Marquee reverse className="-delay-[200ms] [--duration:20s]" repeat={5} > {randomTiles1.map((review, idx) => ( <Card key={idx} {...review} /> ))} </Marquee> <Marquee reverse className="[--duration:30s]" repeat={5}> {randomTiles2.map((review, idx) => ( <Card key={idx} {...review} /> ))} </Marquee> <Marquee reverse className="-delay-[200ms] [--duration:20s]" repeat={5} > {randomTiles3.map((review, idx) => ( <Card key={idx} {...review} /> ))} </Marquee> <Marquee reverse className="[--duration:30s]" repeat={5}> {randomTiles4.map((review, idx) => ( <Card key={idx} {...review} /> ))} </Marquee> <div className="absolute "> <div className="bg-backtround dark:bg-background absolute inset-0 -z-10 rounded-full opacity-40 blur-xl" /> </div> <div className="to-backtround dark:to-background absolute inset-x-0 bottom-0 h-full bg-gradient-to-b from-transparent to-70%" /> </div> </div> </div> </section> ); }CopyExpandCopy and paste the following code into your project.components/eldoraui/marquee.tsximport { cn } from "@/lib/utils"; interface MarqueeProps { className?: string; reverse?: boolean; pauseOnHover?: boolean; children?: React.ReactNode; vertical?: boolean; repeat?: number; [key: string]: any; } export function Marquee({ className, reverse, pauseOnHover = false, children, vertical = false, repeat = 4, ...props }: MarqueeProps) { return ( <div {...props} className={cn( "group flex overflow-hidden p-2 [--duration:40s] [--gap:1rem] [gap:var(--gap)]", { "flex-row": !vertical, "flex-col": vertical, }, className, )} > {Array(repeat) .fill(0) .map((_, i) => ( <div key={i} className={cn("flex shrink-0 justify-around [gap:var(--gap)]", { "animate-marquee flex-row": !vertical, "animate-marquee-vertical flex-col": vertical, "group-hover:[animation-play-state:paused]": pauseOnHover, "[animation-direction:reverse]": reverse, })} > {children} </div> ))} </div> ); }CopyExpandUpdate the import paths to match your project setup.Update tailwind.config.jsAdd the following animations to your tailwind.config.js file:tailwind.config.js/** @type {import('tailwindcss').Config} */ module.exports = { theme: { extend: { animation: { marquee: "marquee var(--duration) linear infinite", "marquee-vertical": "marquee-vertical var(--duration) linear infinite", }, keyframes: { marquee: { from: { transform: "translateX(0)" }, to: { transform: "translateX(calc(-100% - var(--gap)))" }, }, "marquee-vertical": { from: { transform: "translateY(0)" }, to: { transform: "translateY(calc(-100% - var(--gap)))" }, }, }, }, }, };Copy Props Integrations Component Props Prop NameTypeDefaultDescriptioniconJSX.Element-The icon to be displayed inside the card (e.g., from lucide-react).bgJSX.Element-The background element to be displayed behind the icon (a styled div). Internal Variables Variable NameTypeDescriptionrandomTiles1typeof tilesA shuffled array of tile data for the first marquee animation.randomTiles2typeof tilesA shuffled array of tile data for the second marquee animation.randomTiles3typeof tilesA shuffled array of tile data for the third marquee animation.randomTiles4typeof tilesA shuffled array of tile data for the fourth marquee animation. tiles Array Property NameTypeDescriptioniconJSX.ElementThe icon to be displayed inside the card (e.g., from lucide-react).bgJSX.ElementThe background element to be displayed behind the icon (a styled div).