About
CTA section with image and video marquee variants that invites visitors to start with one working agent from daliagents.com, part of the Dali Agents UI.
Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds CTA with Marquee to your project and fits it to your theme.
Code
How CTA with Marquee is used: the demo's code. The component's own source comes with a free account.
Usage.tsx
"use client";
import { cn } from "@/lib/utils";
import Image from "next/image";
import { ReactNode, useState } from "react";
interface MarqueeProps {
children: ReactNode;
pauseOnHover?: boolean;
reverse?: boolean;
className?: string;
speed?: number;
}
function Marquee({
children,
pauseOnHover = false,
reverse = false,
className,
speed = 40,
}: MarqueeProps) {
return (
<div
className={cn(
"group flex overflow-hidden [--gap:0rem] [gap:var(--gap)]",
className
)}
style={
{
"--duration": `${speed}s`,
} as React.CSSProperties
}
>
<div
className={cn(
"flex min-w-full shrink-0 items-center justify-around gap-[var(--gap)] animate-marquee",
reverse && "[animation-direction:reverse]",
pauseOnHover && "group-hover:[animation-play-state:paused]"
)}
>
{children}
</div>
<div
className={cn(
"flex min-w-full shrink-0 items-center justify-around gap-[var(--gap)] animate-marquee",
reverse && "[animation-direction:reverse]",
pauseOnHover && "group-hover:[animation-play-state:paused]"
)}
aria-hidden="true"
>
{children}
</div>
</div>
);
}
const images = [
"https://cdn.21st.dev/assets/mirror/ef/ef19c5a8d215cdff456b7874d70e4069e94ee14df318a443f8ed3180599f061c.jpg",
"https://cdn.21st.dev/assets/mirror/8e/8ee3945e4554269971eee8a787ed55945d692528746dee1d7b50fc06fa0ac9d8.jpg",
"https://cdn.21st.dev/assets/mirror/25/25d37caf50d06441e284d190e362da8cde082042e96ac344b19a6cdd73373549.jpg",
"https://cdn.21st.dev/assets/mirror/2c/2cb5e1165c868beee9a565847d062726e33da007a48b8625ec5239e94db10762.jpg",
];
const images2 = [
"https://cdn.21st.dev/assets/mirror/3b/3b3a5167ff73698abe7c7022f286838cc7db30c7c7a18f58308fb72de7391e36.jpg",
"https://cdn.21st.dev/assets/mirror/50/509a3566233260430784a9c049f3430e402ea5c77f70b1fc6eecbfc21b66fb40.jpg",
"https://cdn.21st.dev/assets/mirror/70/7083136ba27f49f979afcba90b12be3cd5abc9e0d694cf6d90326858f1d1a7b2.jpg",
"https://cdn.21st.dev/assets/mirror/b4/b484922b62223291afa76914a5ebd4ca5bfde61f5244ee2a3e18d2b1a655932d.jpg",
];
function ScrambleButton() {
const [displayText, setDisplayText] = useState("Start with one agent");
const [isScrambling, setIsScrambling] = useState(false);
const originalText = "Start with one agent";
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*";
const scramble = () => {
if (isScrambling) return;
setIsScrambling(true);
let iteration = 0;
const maxIterations = originalText.length;
const interval = setInterval(() => {
setDisplayText(() =>
originalText
.split("")
.map((letter, index) => {
if (index < iteration) {
return originalText[index];
}
return chars[Math.floor(Math.random() * chars.length)];
})
.join("")
);
if (iteration >= maxIterations) {
clearInterval(interval);
setIsScrambling(false);
}
iteration += 1 / 3;
}, 30);
};
return (
<button
onMouseEnter={scramble}
className="px-8 py-3 bg-primary text-primary-foreground rounded-full font-semibold hover:bg-primary/90 transition-colors"
>
{displayText}
</button>
);
}
export default function HeroWithMarqueeLarge() {
return (
<div className="min-h-screen bg-background text-foreground flex items-center overflow-hidden relative">
<div className="w-full">
<div className="flex flex-col lg:flex-row items-center lg:gap-4">
{/* Left Content */}
<div className="flex-shrink-0 space-y-6 px-6 lg:px-12 py-12 lg:py-0 lg:max-w-2xl">
<h1 className="text-4xl md:text-5xl lg:text-6xl font-bold leading-tight">
AI agents that work - or you don't pay
</h1>
<div className="space-y-1 text-muted-foreground">
<p className="text-lg">Fixed-scope packages.</p>
<p className="text-lg">One acceptance test, defined upfront.</p>
</div>
<ScrambleButton />
</div>
{/* Right Marquee Grid */}
<div className="flex-1 w-full lg:w-auto relative space-y-0 overflow-hidden">
<Marquee speed={30} reverse className="[--gap:0rem]">
{images.map((src, idx) => (
<div
key={idx}
className="relative w-64 h-64 lg:w-80 lg:h-80 overflow-hidden flex-shrink-0"
>
<Image
src={src}
alt={`Dali Agents showcase ${idx + 1}`}
fill
className="object-cover"
/>
</div>
))}
</Marquee>
<Marquee speed={30} className="[--gap:0rem]">
{images2.map((src, idx) => (
<div
key={idx}
className="relative w-64 h-64 lg:w-80 lg:h-80 overflow-hidden flex-shrink-0"
>
<Image
src={src}
alt={`Dali Agents showcase ${idx + 5}`}
fill
className="object-cover"
/>
</div>
))}
</Marquee>
{/* Vignette edges */}
<div className="pointer-events-none absolute inset-y-0 left-0 w-32 bg-gradient-to-r from-background to-transparent"></div>
<div className="pointer-events-none absolute inset-y-0 right-0 w-32 bg-gradient-to-l from-background to-transparent"></div>
</div>
</div>
</div>
<a
href="https://daliagents.com"
target="_blank"
rel="noreferrer"
className="absolute bottom-4 left-1/2 -translate-x-1/2 text-[10px] tracking-wide text-muted-foreground/60 transition-colors hover:text-muted-foreground"
>
daliagents.com
</a>
</div>
);
}
















