Liana Pepanyan

CTA with Marquee

Buttons 230

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:1rem] [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/69/697f7681b52a28ac0bdbba7112048ff98b24ae1f61c801524d3bea14a82c40e7.jpg",
  "https://cdn.21st.dev/assets/mirror/4c/4c334511e54d5e842944d4f50d6e652f07665a1e16766d28d11387c9b3b4c4d1.jpg",
  "https://cdn.21st.dev/assets/mirror/89/892cf4ed0c29f61f17fea5cccffad0575e37575ae5742c717ca16274d0d4e894.jpg",
  "https://cdn.21st.dev/assets/mirror/69/690e70500d799ff55c6f30eb47242fbaa63080c2d5d6cdc731048efe8588ebbe.jpg",
];

const images2 = [
  "https://cdn.21st.dev/assets/mirror/c8/c8e98756dd770841764a95ebbdbad40e53854be8a98e1c7d25ddd4c8c7e70255.jpg",
  "https://cdn.21st.dev/assets/mirror/27/278d971ec24e44da1feebce8112db18c9e573b7f1640cf3c2e3465971092df0f.jpg",
  "https://cdn.21st.dev/assets/mirror/ff/ff22309571652c322033406ed025e30c20679bf9c5e7f2dd5d8bebf1acc56dfd.jpg",
  "https://cdn.21st.dev/assets/mirror/7c/7c529e9e13aa7c9933e060b7513d08d6ecceb48f9c276592294b094520115bdb.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 HeroWithMarqueeReverse() {
  return (
    <div className="min-h-screen bg-background text-foreground flex items-center overflow-hidden relative">
      <div className="container mx-auto px-6 lg:px-12">
        <div className="grid lg:grid-cols-2 gap-8 items-center">
          {/* Left Marquee Grid */}
          <div className="space-y-4 overflow-hidden">
            <Marquee speed={30} reverse className="[--gap:1rem]">
              {images.map((src, idx) => (
                <div
                  key={idx}
                  className="relative w-48 h-48 rounded-2xl 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:1rem]">
              {images2.map((src, idx) => (
                <div
                  key={idx}
                  className="relative w-48 h-48 rounded-2xl overflow-hidden flex-shrink-0"
                >
                  <Image
                    src={src}
                    alt={`Dali Agents showcase ${idx + 5}`}
                    fill
                    className="object-cover"
                  />
                </div>
              ))}
            </Marquee>
          </div>

          {/* Right Content */}
          <div className="space-y-6">
            <h1 className="text-4xl md:text-5xl lg:text-6xl font-bold leading-tight">
              Put one agent on a real workflow
            </h1>
            <div className="space-y-1 text-muted-foreground">
              <p className="text-lg">Lead capture, operations, support, voice.</p>
              <p className="text-lg">It works, or you don't pay.</p>
            </div>
            <ScrambleButton />
          </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>
  );
}

Dependencies

More by Liana Pepanyan

View all
  • CTA with Text Marquee

    CTA with Text Marquee

    1.1K
  • Dock

    Dock

    431
  • Hero Minimalism

    Hero Minimalism

    415
  • Dark Grid

    Dark Grid

    207
  • Processing Card

    Processing Card

    198
  • Login / Signup

    Login / Signup

    198
  • Blueprint Shader Grid

    Blueprint Shader Grid

    137
  • Fallback Card

    Fallback Card

    114

More in Buttons

View all
  • Button Group

    Button Group

    2
  • App Store Button

    App Store Button

    457
  • Eclipse Button

    Eclipse Button

    6
  • Texture Button

    Texture Button

    95
  • Button

    Button

    5
  • Button Save

    Button Save

    47
  • Base Button

    Base Button

    3
  • Native Button

    Native Button

    2