Web InnoventixFreeCode

Velocity Band Marquee

Original · free

A skewed dual-direction headline marquee of big gradient and outlined words plus a pill ticker, with speed and pause controls and pause on hover over an animated mesh background.

byWeb InnoventixReact + Tailwind
text marqueeanimated headline marqueeframer motion marqueedual direction marqueespeed control marqueegradient text scroll
Open

Copy prompt gives Claude Code, Cursor or v0 a ready-to-paste prompt that recreates this exact component.

npx shadcn@latest add https://webinnoventix.com/r/marquee-velocity-band.json
marquee-velocity-band.tsx
"use client";

import { useState } from "react";
import { motion } from "motion/react";

const words = ["Design", "Prototype", "Automate", "Ship", "Scale", "Iterate", "Launch"];
const pills = [
  "No code required",
  "Realtime sync",
  "Dark mode ready",
  "Fully responsive",
  "Open API",
  "Team ready",
  "Blazing fast",
];

function Sep() {
  return (
    <svg viewBox="0 0 24 24" className="mx-6 h-6 w-6 shrink-0 fill-indigo-500/70 sm:h-8 sm:w-8" aria-hidden="true">
      <path d="M12 2l2.4 7.1L22 9.5l-6 4.6 2.3 7.4L12 17.4 5.7 21.5 8 14.1l-6-4.6 7.6-.4z" />
    </svg>
  );
}

function WordTrack({ dir, duration }: { dir: "left" | "right"; duration: number }) {
  const doubled = [...words, ...words];
  return (
    <div className="relative overflow-hidden py-2">
      <div
        className={`vb-track vb-${dir} items-center`}
        style={{ animationDuration: `${duration}s` }}
      >
        {doubled.map((w, i) => {
          const filled = i % 2 === 0;
          return (
            <span key={`w-${i}`} className="flex shrink-0 items-center" aria-hidden={i >= words.length ? true : undefined}>
              <span
                className={
                  filled
                    ? "vb-sheen bg-gradient-to-r from-indigo-400 via-fuchsia-400 to-amber-300 bg-clip-text text-4xl font-black tracking-tight text-transparent sm:text-6xl"
                    : "cursor-default text-4xl font-black tracking-tight text-transparent transition-all duration-300 [-webkit-text-stroke:1.5px_rgb(212,212,216)] hover:text-white hover:[-webkit-text-stroke:0px_transparent] sm:text-6xl dark:[-webkit-text-stroke:1.5px_rgb(82,82,91)]"
                }
              >
                {w}
              </span>
              <Sep />
            </span>
          );
        })}
      </div>
    </div>
  );
}

function PillTrack({ dir, duration }: { dir: "left" | "right"; duration: number }) {
  const doubled = [...pills, ...pills];
  return (
    <div className="relative overflow-hidden py-2">
      <div
        className={`vb-track vb-${dir} items-center gap-3`}
        style={{ animationDuration: `${duration}s` }}
      >
        {doubled.map((p, i) => (
          <span
            key={`p-${i}`}
            aria-hidden={i >= pills.length ? true : undefined}
            className="flex shrink-0 items-center gap-2 rounded-full border border-white/15 bg-white/5 px-5 py-2.5 text-sm font-medium text-zinc-200 backdrop-blur transition-colors hover:border-indigo-400/50 hover:bg-indigo-500/10 hover:text-white"
          >
            <svg viewBox="0 0 24 24" className="h-4 w-4 fill-none stroke-indigo-400" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
              <path d="M20 6L9 17l-5-5" />
            </svg>
            {p}
          </span>
        ))}
      </div>
    </div>
  );
}

export default function MarqueeVelocityBand() {
  const [fast, setFast] = useState(false);
  const [paused, setPaused] = useState(false);
  const k = fast ? 0.5 : 1;

  return (
    <section className={`relative overflow-hidden bg-zinc-950 px-6 py-20 md:py-28 ${paused ? "vb-paused" : ""}`}>
      <style>{`
        @keyframes vb-scroll-left { from { transform: translateX(0); } to { transform: translateX(-50%); } }
        @keyframes vb-scroll-right { from { transform: translateX(-50%); } to { transform: translateX(0); } }
        @keyframes vb-sweep { 0% { background-position: 0% 50%; } 100% { background-position: 200% 50%; } }
        @keyframes vb-mesh { 0%,100% { transform: translate(0,0) scale(1); } 50% { transform: translate(5%,4%) scale(1.2); } }
        @keyframes vb-mesh-2 { 0%,100% { transform: translate(0,0) scale(1.15); } 50% { transform: translate(-6%,-5%) scale(0.9); } }
        .vb-track { display: flex; width: max-content; animation-timing-function: linear; animation-iteration-count: infinite; will-change: transform; }
        .vb-left { animation-name: vb-scroll-left; }
        .vb-right { animation-name: vb-scroll-right; }
        .vb-band:hover .vb-track { animation-play-state: paused; }
        .vb-paused .vb-track { animation-play-state: paused; }
        .vb-sheen { background-size: 200% auto; animation: vb-sweep 5s linear infinite; }
        .vb-mesh { animation: vb-mesh 16s ease-in-out infinite; }
        .vb-mesh-2 { animation: vb-mesh-2 20s ease-in-out infinite; }
        @media (prefers-reduced-motion: reduce) {
          .vb-track, .vb-sheen, .vb-mesh, .vb-mesh-2 { animation: none; }
        }
      `}</style>

      <div aria-hidden="true" className="pointer-events-none absolute inset-0 overflow-hidden">
        <div className="vb-mesh absolute -left-20 top-0 h-96 w-96 rounded-full bg-indigo-600/30 blur-[100px]" />
        <div className="vb-mesh-2 absolute -right-20 bottom-0 h-96 w-96 rounded-full bg-fuchsia-600/25 blur-[100px]" />
        <div className="absolute inset-0 bg-[radial-gradient(circle_at_center,transparent_55%,rgb(9,9,11)_100%)]" />
        <div
          className="absolute inset-0 opacity-[0.04]"
          style={{ backgroundImage: "linear-gradient(rgb(255,255,255) 1px,transparent 1px),linear-gradient(90deg,rgb(255,255,255) 1px,transparent 1px)", backgroundSize: "48px 48px" }}
        />
      </div>

      <div className="relative mx-auto max-w-3xl text-center">
        <motion.span
          initial={{ opacity: 0, y: 14 }}
          whileInView={{ opacity: 1, y: 0 }}
          viewport={{ once: true, margin: "-80px" }}
          transition={{ duration: 0.5, ease: [0.22, 1, 0.36, 1] }}
          className="inline-flex items-center gap-2 rounded-full border border-white/15 bg-white/5 px-4 py-1.5 text-xs font-semibold uppercase tracking-widest text-indigo-300 backdrop-blur"
        >
          Built for momentum
        </motion.span>
        <motion.h2
          initial={{ opacity: 0, y: 18 }}
          whileInView={{ opacity: 1, y: 0 }}
          viewport={{ once: true, margin: "-80px" }}
          transition={{ duration: 0.55, delay: 0.08, ease: [0.22, 1, 0.36, 1] }}
          className="mt-5 text-balance text-3xl font-bold tracking-tight text-white sm:text-4xl"
        >
          Everything you need to move fast
        </motion.h2>

        <motion.div
          initial={{ opacity: 0, y: 18 }}
          whileInView={{ opacity: 1, y: 0 }}
          viewport={{ once: true, margin: "-80px" }}
          transition={{ duration: 0.55, delay: 0.16, ease: [0.22, 1, 0.36, 1] }}
          className="mt-6 flex items-center justify-center gap-3"
        >
          <motion.button
            type="button"
            onClick={() => setFast((v) => !v)}
            aria-pressed={fast}
            whileTap={{ scale: 0.94 }}
            className="inline-flex items-center gap-2 rounded-full border border-white/15 bg-white/5 px-4 py-2 text-sm font-semibold text-white backdrop-blur transition-colors hover:border-indigo-400/50 hover:bg-indigo-500/15"
          >
            <svg viewBox="0 0 24 24" className="h-4 w-4 fill-indigo-300" aria-hidden="true">
              <path d="M13 2L4.5 13.5H11l-1 8.5 8.5-11.5H12z" />
            </svg>
            {fast ? "Warp speed" : "Cruise"}
          </motion.button>
          <motion.button
            type="button"
            onClick={() => setPaused((v) => !v)}
            aria-pressed={paused}
            whileTap={{ scale: 0.94 }}
            className="inline-flex items-center gap-2 rounded-full border border-white/15 bg-white/5 px-4 py-2 text-sm font-semibold text-white backdrop-blur transition-colors hover:border-indigo-400/50 hover:bg-indigo-500/15"
          >
            {paused ? (
              <svg viewBox="0 0 24 24" className="h-4 w-4 fill-indigo-300" aria-hidden="true">
                <path d="M8 5v14l11-7z" />
              </svg>
            ) : (
              <svg viewBox="0 0 24 24" className="h-4 w-4 fill-indigo-300" aria-hidden="true">
                <path d="M7 5h4v14H7zM13 5h4v14h-4z" />
              </svg>
            )}
            {paused ? "Play" : "Pause"}
          </motion.button>
        </motion.div>
      </div>

      <motion.div
        initial={{ opacity: 0, y: 30 }}
        whileInView={{ opacity: 1, y: 0 }}
        viewport={{ once: true, margin: "-60px" }}
        transition={{ duration: 0.6, delay: 0.2, ease: [0.22, 1, 0.36, 1] }}
        className="relative mt-14"
        aria-label="Product capabilities marquee"
      >
        <div className="vb-band -rotate-2">
          <div className="relative w-[106%] -translate-x-[3%] [-webkit-mask-image:linear-gradient(to_right,transparent,black_8%,black_92%,transparent)] [mask-image:linear-gradient(to_right,transparent,black_8%,black_92%,transparent)]">
            <WordTrack dir="left" duration={26 * k} />
            <div className="my-3 h-px w-full bg-gradient-to-r from-transparent via-white/15 to-transparent" />
            <PillTrack dir="right" duration={30 * k} />
          </div>
        </div>
      </motion.div>
    </section>
  );
}

Dependencies

motion

Licence

Built by Web Innoventix. Free for personal and commercial use, no attribution required.

Built by Web Innoventix

Need a custom interface, a full website, or SEO that gets you cited by AI? We design, build and rank it end to end.

Get a free quote

Similar components

Browse all →
Glowing Logo Marquee

Glowing Logo Marquee

Original

A dual-row infinite logo marquee whose rows scroll in opposite directions, pause on hover and lift each logo with a shimmer sweep over an animated gradient glow.

Vertical Testimonial Marquee

Vertical Testimonial Marquee

Original

Three vertical testimonial columns scroll at different speeds and opposite directions, pausing on hover with a shine sweep so reviews loop endlessly beside a sticky heading.

Spotlight Hero

Spotlight Hero

Original

A centred hero with a soft radial spotlight, badge and dual call-to-action.

Split Hero

Split Hero

Original

A two-column hero pairing a headline and CTAs with a product mock and social proof.

Gradient Spotlight Hero

Gradient Spotlight Hero

Original

A minimal centred hero with a soft gradient-mesh backdrop, announcement pill and dual call-to-action buttons.

App Preview Hero

App Preview Hero

Original

A centred hero that pairs headline copy with a realistic product dashboard mock built entirely from markup, complete with browser chrome and a floating notification card.

Waitlist Capture Hero

Waitlist Capture Hero

Original

A dark, focused hero with an inline email capture form and avatar social proof, ready for pre-launch waitlists.

Image Backdrop Hero

Image Backdrop Hero

Original

A cinematic full-bleed hero with a layered image-style backdrop, overlaid left-aligned copy and a trusted-by logos strip.

Gradient Mesh Hero with Staggered Text Reveal

Gradient Mesh Hero with Staggered Text Reveal

Original

A full-screen hero pairing a drifting animated gradient-mesh backdrop with a word-by-word staggered blur-in headline and a logo marquee under the fold.

Typewriter Rotating Words Hero

Typewriter Rotating Words Hero

Original

A hero whose headline cycles through rotating words with a live typewriter caret over an animated grid backdrop, degrading to a gentle word swap when reduced motion is on.

Floating UI Cards Hero with Mouse Parallax

Floating UI Cards Hero with Mouse Parallax

Original

A hero with glassy floating UI cards that drift continuously and shift on mouse-parallax depth around a staggered centre headline.

Aurora Hero with Shimmer CTA and Logo Marquee

Aurora Hero with Shimmer CTA and Logo Marquee

Original

A dark hero with animated aurora light beams, a shimmering sweep across the primary CTA, and dual-direction logo marquees beneath the fold.