Marquee Rails Components

Smooth scrolling animations for showcasing logos, testimonials, or any repeating content. Perfect for partner showcases and brand displays.

Installation

1. Stimulus Controller Setup

Start by adding the following controller to your project:

import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static targets = ["track", "list"];
  static values = {
    speed: { type: Number, default: 20 }, // Speed of the animation (the lower the number, the faster the animation)
    hoverSpeed: { type: Number, default: 0 }, // Speed when hovered (the lower the number, the slower the animation)
    direction: { type: String, default: "left" }, // left or right
    clones: { type: Number, default: 2 }, // Number of clones to create
  };

  connect() {
    this.isPaused = false;
    this.setupMarquee();

    // Restart animation when content changes
    this.resizeObserver = new ResizeObserver(() => {
      this.restartAnimation();
    });
    this.resizeObserver.observe(this.listTarget);
  }

  disconnect() {
    this.cleanupAnimation();
    if (this.resizeObserver) {
      this.resizeObserver.disconnect();
    }
  }

  setupMarquee() {
    // Remove any existing clones first
    const existingClones = this.trackTarget.querySelectorAll(".marquee-clone");
    existingClones.forEach((clone) => clone.remove());

    // Ensure the list has proper flex properties
    this.listTarget.style.flexShrink = "0";

    // Create multiple clones for seamless loop
    for (let i = 0; i < this.clonesValue; i++) {
      const clone = this.listTarget.cloneNode(true);
      clone.setAttribute("aria-hidden", "true");
      clone.classList.add("marquee-clone");
      clone.style.flexShrink = "0";

      // Remove data-marquee-target from clone to avoid Stimulus conflicts
      clone.removeAttribute("data-marquee-target");

      // Append clone after the previous content
      this.trackTarget.appendChild(clone);
    }

    // Ensure track is set up correctly for side-by-side display
    this.trackTarget.style.display = "flex";
    this.trackTarget.style.flexWrap = "nowrap";

    // For right direction, we might need to adjust the initial position
    if (this.directionValue === "right") {
      // Start from the cloned content for right direction
      const contentWidth = this.listTarget.offsetWidth;
      this.trackTarget.style.transform = `translateX(-${contentWidth}px)`;
    }

    // Add smooth transition for transform changes (only for speed changes)
    this.trackTarget.style.transition = "transform 0.5s ease-out";

    // Setup CSS animation
    requestAnimationFrame(() => {
      this.applyAnimation();
    });
  }

  applyAnimation() {
    // Get the actual width of one set of content
    const contentWidth = this.listTarget.offsetWidth;

    // Create unique animation name for this instance
    const animationId = `marquee-${this.element.dataset.marqueeId || Date.now()}`;
    const animationName = `${animationId}-${this.directionValue}`;

    // Reset transform before applying animation
    this.trackTarget.style.transform = "";

    // Apply animation to track
    this.trackTarget.style.animation = `${animationName} ${this.speedValue}s linear infinite`;

    // Inject keyframes with the actual content width
    this.injectKeyframes(animationName, contentWidth);
  }

  injectKeyframes(animationName, contentWidth) {
    // Remove any existing keyframes for this animation
    const existingStyle = document.getElementById(animationName);
    if (existingStyle) {
      existingStyle.remove();
    }

    const style = document.createElement("style");
    style.id = animationName;

    // The animation moves by exactly one content width
    // When it completes, it jumps back to start, but since we have clones,
    // this jump is invisible to the user
    if (this.directionValue === "left") {
      style.textContent = `
        @keyframes ${animationName} {
          0% { transform: translateX(0); }
          100% { transform: translateX(-${contentWidth}px); }
        }
      `;
    } else {
      // For right direction, we animate from -contentWidth to 0
      style.textContent = `
        @keyframes ${animationName} {
          0% { transform: translateX(-${contentWidth}px); }
          100% { transform: translateX(0); }
        }
      `;
    }

    document.head.appendChild(style);

    // Also inject base styles if not already present
    this.injectBaseStyles();
  }

  injectBaseStyles() {
    const baseStyleId = "marquee-base-styles";

    if (document.getElementById(baseStyleId)) return;

    const style = document.createElement("style");
    style.id = baseStyleId;
    style.textContent = `
      /* Ensure smooth rendering */
      [data-marquee-target="track"] {
        will-change: transform;
        backface-visibility: hidden;
        perspective: 1000px;
      }

      /* Override transition during animation to prevent interference */
      [data-marquee-target="track"]:not(.marquee-transitioning) {
        transition: none !important;
      }

      /* Ensure original and clone are displayed side by side */
      [data-marquee-target="list"],
      .marquee-clone {
        flex-shrink: 0;
        min-width: max-content;
      }
    `;

    document.head.appendChild(style);
  }

  pauseAnimation() {
    if (this.hoverSpeedValue === 0) {
      // Pause completely
      this.trackTarget.style.animationPlayState = "paused";
      this.isPaused = true;
    } else {
      // Smoothly slow down
      this.smoothSpeedChange(this.hoverSpeedValue);
    }
  }

  resumeAnimation() {
    if (this.hoverSpeedValue === 0 && this.isPaused) {
      // Resume from pause
      this.trackTarget.style.animationPlayState = "running";
      this.isPaused = false;
    } else if (this.hoverSpeedValue > 0) {
      // Return to normal speed
      this.smoothSpeedChange(this.speedValue);
    }
  }

  smoothSpeedChange(newSpeed) {
    // Get current transform
    const currentTransform = window.getComputedStyle(this.trackTarget).transform;

    // Add transitioning class
    this.trackTarget.classList.add("marquee-transitioning");

    // Apply current position and remove animation
    this.trackTarget.style.animation = "none";
    this.trackTarget.style.transform = currentTransform;

    // Force reflow
    void this.trackTarget.offsetHeight;

    // Remove transitioning class and reapply animation with new speed
    requestAnimationFrame(() => {
      this.trackTarget.classList.remove("marquee-transitioning");

      // Get content width for accurate animation
      const contentWidth = this.listTarget.offsetWidth;
      const animationId = `marquee-${this.element.dataset.marqueeId || Date.now()}`;
      const animationName = `${animationId}-${this.directionValue}`;

      this.trackTarget.style.animation = `${animationName} ${newSpeed}s linear infinite`;

      // Update keyframes if needed
      this.injectKeyframes(animationName, contentWidth);

      // Calculate progress to maintain position
      const matrix = new DOMMatrix(currentTransform);
      const currentX = matrix.m41;

      // Calculate progress through the animation cycle
      let progress;
      if (this.directionValue === "left") {
        // For left: 0 to -contentWidth
        progress = Math.abs(currentX) / contentWidth;
      } else {
        // For right: -contentWidth to 0
        progress = (contentWidth + currentX) / contentWidth;
      }

      // Ensure progress is between 0 and 1
      progress = Math.max(0, Math.min(1, progress));

      // Apply negative delay to start from current position
      this.trackTarget.style.animationDelay = `${-progress * newSpeed}s`;
    });
  }

  restartAnimation() {
    if (!this.hasTrackTarget || !this.hasListTarget) return;

    // Clear animation
    this.trackTarget.style.animation = "none";

    // Force reflow
    void this.trackTarget.offsetHeight;

    // Re-setup marquee
    this.setupMarquee();
  }

  cleanupAnimation() {
    // Remove cloned elements
    const clones = this.trackTarget.querySelectorAll(".marquee-clone");
    clones.forEach((clone) => clone.remove());

    // Remove animation styles for this instance
    const animationId = `marquee-${this.element.dataset.marqueeId || Date.now()}`;
    const styles = document.querySelectorAll(`[id^="${animationId}"]`);
    styles.forEach((style) => style.remove());
  }

  // Value change callbacks
  speedValueChanged() {
    if (this.hasTrackTarget && !this.isPaused) {
      this.smoothSpeedChange(this.speedValue);
    }
  }

  directionValueChanged() {
    if (this.hasTrackTarget) {
      this.restartAnimation();
    }
  }

  clonesValueChanged() {
    if (this.hasTrackTarget) {
      this.restartAnimation();
    }
  }
}

Examples

Basic Marquee

A smooth scrolling marquee that continuously moves content from right to left. Hover to pause the animation.

Ruby on Rails
Stimulus
Tailwind CSS
Javascript
<div class="w-full">
  <div
    data-controller="marquee"
    data-marquee-speed-value="15"
    class="relative overflow-hidden bg-transparent py-4"
    data-action="mouseenter->marquee#pauseAnimation mouseleave->marquee#resumeAnimation"
  >
    <div data-marquee-target="track" class="relative flex w-full">
      <div data-marquee-target="list" class="flex w-full shrink-0 flex-nowrap items-center justify-around gap-8 px-4">
        <div class="flex items-center space-x-2">
          <svg xmlns="http://www.w3.org/2000/svg" fill="#d30001" viewBox="0 0 235 155" class="h-5">
            <g>
              <path d="M180 17.8c-.9-.5-3.4-1.7-9.7-3.5l-.4 6.6c3.3 1.1 6.5 2.3 9.7 3.6l.4-6.7Z"></path>
              <path d="M124.9 18.9c-29.2 2.6-65 29.1-86 64.1S15 147.6 15 147.6h79s-15.1-68.9 34.9-96.8c10.9-5.3 45.6-25.1 102.4 16.9 1.8-1.5 3.5-2.7 3.5-2.7s-52-51.9-109.9-46.1Z"></path>
              <path d="m54.7 54.7-7.1-6.2c-2.6 2.5-5.1 5-7.4 7.5l7.7 6.6c2.1-2.7 4.4-5.4 6.8-7.9Z"></path>
              <path d="M92.5 25 88 18.1c-2.5 1.3-5.1 2.7-7.8 4.3l4.6 7c2.6-1.6 5.1-3.1 7.7-4.4Z"></path>
              <path d="M24.5 99.4 13 95.2c-1.9 4.3-4 9.3-5 12l11.5 4.2c1.3-3.4 3.4-8.3 5-12Z"></path>
              <path d="M133.1 14.5h1l-2-6.1c-3.1 0-6.3.2-9.6.6l1.9 5.9c2.9-.3 5.8-.4 8.7-.4Z"></path>
              <path d="M97 127.6c.2 5.3.7 9.6 1.2 12.6l12 4.3c-.9-3.9-1.8-8.3-2.4-13L97 127.6Z"></path>
              <path d="m137.9 51.3 2.3 6.9c2.9-1.4 5.8-2.6 8.7-3.5l-2.2-6.6c-3.4 1-6.3 2.1-8.8 3.2Z"></path>
              <path d="M169.7 52c3.3.1 6.6.5 9.9 1.2l.4-6.2c-3.4-.7-6.7-1.1-9.9-1.3l-.4 6.3Z"></path>
              <path d="M101.5 91.2c-1.3 3.7-2.2 7.4-3 11l8.1 6.4c.4-3.9 1.1-7.8 2.1-11.7l-7.2-5.7Z"></path>
              <path d="m113 70 4.8 7.2c1.7-2.5 3.7-4.8 5.9-7.1l-4.5-6.8c-2.3 2.1-4.4 4.4-6.2 6.7Z"></path>
            </g>
          </svg>
          <span class="text-sm font-medium text-neutral-800 dark:text-neutral-200">Ruby on Rails</span>
        </div>
        <div class="flex items-center space-x-2">
          <svg class="size-4 rounded-sm" width="60" height="60" viewBox="0 0 60 60" xmlns="http://www.w3.org/2000/svg">
            <g class="fill-neutral-800 dark:fill-white">
              <path d="M1.76 1.85885e-08H58.2399C58.7016 -6.70275e-05 59.1447 0.181237 59.474 0.50484C59.8032 0.828443 59.9921 1.26844 59.9999 1.73V7.37823C59.9999 7.87239 59.9999 8.11947 59.9038 8.30822C59.8192 8.47424 59.6842 8.60923 59.5182 8.69382C59.3294 8.78999 59.0823 8.78999 58.5882 8.78999H45.92C44.7722 8.83556 43.6502 9.14343 42.64 9.68999L15.64 25.55C14.6261 26.0964 13.5008 26.4042 12.35 26.45H1.41176C0.9176 26.45 0.670518 26.45 0.481773 26.3538C0.315747 26.2692 0.180765 26.1342 0.0961706 25.9682C0 25.7795 0 25.5324 0 25.0382V17.2717C0 16.7776 0 16.5305 0.0961706 16.3418C0.180765 16.1757 0.315747 16.0408 0.481773 15.9562C0.670518 15.86 0.9176 15.86 1.41176 15.86H12.35C13.5001 15.9106 14.6243 16.2181 15.64 16.76L18.64 18.51C19.1037 18.7494 19.6181 18.8743 20.14 18.8743C20.6619 18.8743 21.1762 18.7494 21.64 18.51L25.48 16.25C25.6438 16.1661 25.7812 16.0386 25.8772 15.8815C25.9732 15.7245 26.024 15.544 26.024 15.36C26.024 15.1759 25.9732 14.9955 25.8772 14.8384C25.7812 14.6814 25.6438 14.5539 25.48 14.47L17.4 9.71999C16.3879 9.17798 15.2669 8.8704 14.12 8.81999H1.41176C0.9176 8.81999 0.670518 8.81999 0.481773 8.72382C0.315747 8.63923 0.180765 8.50424 0.0961706 8.33822C0 8.14947 0 7.90239 0 7.40823V1.76C0 1.29322 0.185428 0.845555 0.515492 0.515492C0.845555 0.185428 1.29322 1.85885e-08 1.76 1.85885e-08Z"></path>
              <path d="M47.65 15.8799C46.4998 15.9305 45.3757 16.238 44.36 16.7799L17.36 32.6499C16.3479 33.1919 15.2269 33.4994 14.08 33.5499H1.41176C0.9176 33.5499 0.670518 33.5499 0.481773 33.646C0.315747 33.7306 0.180765 33.8656 0.0961706 34.0316C0 34.2204 0 34.4674 0 34.9616V42.7281C0 43.2222 0 43.4693 0.0961706 43.6581C0.180765 43.8241 0.315747 43.9591 0.481773 44.0437C0.670518 44.1398 0.9176 44.1398 1.41176 44.1398H12.35C13.5001 44.0892 14.6243 43.7817 15.64 43.2398L42.64 27.3699C43.652 26.8278 44.773 26.5203 45.92 26.4699H58.5882C59.0823 26.4699 59.3294 26.4699 59.5182 26.3737C59.6842 26.2891 59.8192 26.1541 59.9038 25.9881C59.9999 25.7993 59.9999 25.5523 59.9999 25.0581V17.2916C59.9999 16.7975 59.9999 16.5504 59.9038 16.3616C59.8192 16.1956 59.6842 16.0606 59.5182 15.976C59.3294 15.8799 59.0823 15.8799 58.5882 15.8799H47.65Z"></path>
              <path d="M47.65 33.56C46.4998 33.6106 45.3757 33.9182 44.36 34.46L17.36 50.32C16.3497 50.8666 15.2277 51.1744 14.08 51.22H1.41176C0.9176 51.22 0.670518 51.22 0.481773 51.3162C0.315747 51.4008 0.180765 51.5358 0.0961706 51.7018C0 51.8905 0 52.1376 0 52.6318V58.28C0.0129646 58.739 0.203789 59.175 0.532182 59.4959C0.860576 59.8168 1.30083 59.9976 1.76 60H58.2399C58.7042 59.9974 59.1488 59.8125 59.4781 59.4852C59.8073 59.1578 59.9947 58.7142 59.9999 58.25V52.6018C59.9999 52.1076 59.9999 51.8605 59.9038 51.6718C59.8192 51.5058 59.6842 51.3708 59.5182 51.2862C59.3294 51.19 59.0823 51.19 58.5882 51.19H45.88C44.7322 51.1444 43.6102 50.8366 42.6 50.29L34.52 45.55C34.3536 45.4671 34.2136 45.3394 34.1157 45.1813C34.0179 45.0232 33.966 44.8409 33.966 44.655C33.966 44.4691 34.0179 44.2868 34.1157 44.1287C34.2136 43.9706 34.3536 43.843 34.52 43.76L38.36 41.51C38.8237 41.2706 39.3381 41.1457 39.86 41.1457C40.3819 41.1457 40.8962 41.2706 41.36 41.51L44.36 43.25C45.3739 43.7964 46.4991 44.1042 47.65 44.15H58.5882C59.0823 44.15 59.3294 44.15 59.5182 44.0538C59.6842 43.9693 59.8192 43.8343 59.9038 43.6682C59.9999 43.4795 59.9999 43.2324 59.9999 42.7383V34.9718C59.9999 34.4776 59.9999 34.2305 59.9038 34.0418C59.8192 33.8758 59.6842 33.7408 59.5182 33.6562C59.3294 33.56 59.0823 33.56 58.5882 33.56H47.65Z"></path>
            </g>
          </svg>
          <span class="text-sm font-medium text-neutral-800 dark:text-neutral-200">Stimulus</span>
        </div>
        <div class="flex items-center space-x-2">
          <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20" class="h-5"><path fill="#23B2E7" fill-rule="evenodd" d="M10 5Q6 5 5 8.334q1.5-1.668 3.5-1.25c.76.158 1.305.618 1.906 1.127C11.386 9.041 12.522 10 15 10q4 0 5-3.333-1.5 1.667-3.5 1.25c-.76-.159-1.305-.619-1.906-1.128C13.614 5.96 12.479 5 10 5m-5 5q-4 0-5 3.334 1.5-1.668 3.5-1.25c.76.158 1.305.618 1.906 1.127C6.386 14.041 7.521 15 10 15q4 0 5-3.333-1.5 1.667-3.5 1.25c-.76-.159-1.305-.619-1.906-1.128C8.614 10.96 7.478 10 5 10" clip-rule="evenodd"></path></svg>
          <span class="text-sm font-medium text-neutral-800 dark:text-neutral-200">Tailwind CSS</span>
        </div>
        <div class="flex items-center space-x-2">
          <svg xmlns="http://www.w3.org/2000/svg" class="size-4 rounded-sm" viewBox="0 0 630 630">
            <rect width="630" height="630" fill="#f7df1e"/>
            <path d="m423.2 492.19c12.69 20.72 29.2 35.95 58.4 35.95 24.53 0 40.2-12.26 40.2-29.2 0-20.3-16.1-27.49-43.1-39.3l-14.8-6.35c-42.72-18.2-71.1-41-71.1-89.2 0-44.4 33.83-78.2 86.7-78.2 37.64 0 64.7 13.1 84.2 47.4l-46.1 29.6c-10.15-18.2-21.1-25.37-38.1-25.37-17.34 0-28.33 11-28.33 25.37 0 17.76 11 24.95 36.4 35.95l14.8 6.34c50.3 21.57 78.7 43.56 78.7 93 0 53.3-41.87 82.5-98.1 82.5-54.98 0-90.5-26.2-107.88-60.54zm-209.13 5.13c9.3 16.5 17.76 30.45 38.1 30.45 19.45 0 31.72-7.61 31.72-37.2v-201.3h59.2v202.1c0 61.3-35.94 89.2-88.4 89.2-47.4 0-74.85-24.53-88.81-54.075z"/>
          </svg>
          <span class="text-sm font-medium text-neutral-800 dark:text-neutral-200">Javascript</span>
        </div>
      </div>
    </div>
    <div class="flex pointer-events-none absolute inset-0 justify-between items-center">
      <div class="relative h-full w-full">
        <!-- Left gradient do define depending your page background color -->
        <div class="w-5 bg-gradient-to-r from-white dark:from-[#080808] to-transparent absolute left-0 inset-y-0"></div>
        <!-- Right gradient do define depending your page background color -->
        <div class="w-5 bg-gradient-to-l from-white dark:from-[#080808] to-transparent absolute right-0 inset-y-0"></div>
      </div>
    </div>
  </div>
</div>

Multi-Row Marquee

Multiple rows with different directions and speeds. Hover to pause individual rows.

You're in good company

Trusted by leading brands

Trello
Apple
Behance
Microsoft
Slack
Patreon
Pinterest
YouTube
Framer
Spotify
Discord
<div class="relative w-full py-16">
  <div class="mx-auto">
    <div class="mx-auto max-w-2xl text-center">
      <h2 class="text-base leading-7 font-medium text-neutral-500 dark:text-neutral-400">You're in good company</h2>
      <p class="mt-2 text-xl font-bold tracking-tight text-neutral-900 sm:text-2xl lg:text-3xl dark:text-white">Trusted by leading brands</p>
    </div>
  </div>

  <!-- Multi-row Marquee Container -->
  <div class="mt-8 overflow-hidden">
    <!-- First Row - Left Direction -->
    <div data-controller="marquee" data-marquee-hover-speed-value="0" data-marquee-speed-value="30" data-marquee-direction-value="left" class="relative mb-8 overflow-hidden" data-action="mouseenter->marquee#pauseAnimation mouseleave->marquee#resumeAnimation">
      <!-- Marquee Track -->
      <div data-marquee-target="track" class="relative flex w-full">
        <!-- Marquee List -->
        <div data-marquee-target="list" class="flex shrink-0 flex-nowrap items-center gap-6 px-3 py-1">
          <!-- Brand 1 -->
          <div class="flex min-w-[150px] items-center justify-center rounded-xl border border-black/5 bg-neutral-50 px-6 py-4 shadow-xs transition-all hover:bg-neutral-100 dark:border-white/10 dark:bg-neutral-800 dark:hover:bg-neutral-700">
            <div class="flex items-center space-x-3">
              <svg xmlns="http://www.w3.org/2000/svg" class="size-6" width="32" height="32" viewBox="0 0 32 32">
                <g fill="currentColor"><path d="M25.906,3H6.089c-1.707,.002-3.089,1.387-3.089,3.094V25.911c0,1.706,1.383,3.089,3.089,3.089H25.906c1.707,0,3.091-1.383,3.094-3.089V6.094c-.002-1.708-1.386-3.091-3.094-3.094ZM14.197,21.729c.002,.566-.454,1.026-1.02,1.028-.001,0-.003,0-.004,0h-4.348c-.566,0-1.024-.459-1.024-1.024,0-.001,0-.003,0-.004V8.829c-.002-.566,.454-1.026,1.02-1.028,.001,0,.003,0,.004,0h4.348c.566,0,1.024,.459,1.024,1.024,0,.001,0,.003,0,.004v12.9Zm10.002-5.923c0,.568-.46,1.028-1.028,1.028h-4.344c-.568,0-1.028-.46-1.028-1.028v-6.977c0-.568,.46-1.028,1.028-1.028h4.344c.568,0,1.028,.46,1.028,1.028v6.977Z" fill-rule="evenodd"></path></g>
              </svg>
              <span class="text-lg font-medium text-neutral-800 dark:text-neutral-200">Trello</span>
            </div>
          </div>

          <!-- Brand 2 -->
          <div class="flex min-w-[150px] items-center justify-center rounded-xl border border-black/5 bg-neutral-50 px-6 py-4 shadow-xs transition-all hover:bg-neutral-100 dark:border-white/10 dark:bg-neutral-800 dark:hover:bg-neutral-700">
            <div class="flex items-center space-x-3">
              <svg xmlns="http://www.w3.org/2000/svg" class="size-6" width="24" height="24" viewBox="0 0 24 24">
                <g fill="currentColor">
                  <path fill-rule="evenodd" clip-rule="evenodd" d="M13.3091 6.48971C13.8886 6.26249 14.614 6.00143 15.6913 6.00143C17.3749 6.00143 19.1523 6.89003 20.3404 8.35613L21.0976 9.29055L20.0406 9.86449C17.5093 11.2391 17.8608 14.8633 20.4912 15.8476L21.4953 16.2234L21.0506 17.199C20.5752 18.2418 20.3284 18.7499 19.6885 19.7119C18.7074 21.192 17.3565 22.9787 15.3958 22.997C14.1793 23.0084 13.2116 22.1656 12.0053 22.172C10.7909 22.1784 9.80329 23 8.57946 22.9999L8.57073 22.9999C6.64549 22.9829 5.31146 21.3315 4.3393 19.8664C1.80437 16.0461 1.46451 11.568 3.19812 8.92508C4.43251 7.04322 6.3164 6.00413 8.20878 6.00413C9.15354 6.00413 9.91949 6.26847 10.5172 6.47873C11.1502 6.70144 11.5462 6.84 11.9823 6.84C12.4376 6.84 12.7407 6.71256 13.3091 6.48971Z" fill="currentColor"></path>
                  <path d="M15.3653 3.793C16.0815 2.87425 16.5703 1.63176 16.373 0.305766C15.203 0.386016 13.89 1.07576 13.0913 2.0455C12.3668 2.92525 11.7675 4.231 12 5.5C13.2765 5.53975 14.598 4.77775 15.3653 3.793Z" fill="currentColor"></path>
                </g>
              </svg>
              <span class="text-lg font-medium text-neutral-800 dark:text-neutral-200">Apple</span>
            </div>
          </div>

          <!-- Brand 3 -->
          <div class="flex min-w-[150px] items-center justify-center rounded-xl border border-black/5 bg-neutral-50 px-6 py-4 shadow-xs transition-all hover:bg-neutral-100 dark:border-white/10 dark:bg-neutral-800 dark:hover:bg-neutral-700">
            <div class="flex items-center space-x-3">
              <svg xmlns="http://www.w3.org/2000/svg" class="size-6" width="32" height="32" viewBox="0 0 32 32">
                <g fill="currentColor"><path d="M10.144,7.203c.808,0,1.554,.062,2.238,.249,.684,.124,1.243,.373,1.741,.684s.87,.746,1.119,1.306c.249,.56,.373,1.243,.373,1.989,0,.87-.187,1.616-.622,2.176-.373,.56-.995,1.057-1.741,1.43,1.057,.311,1.865,.87,2.362,1.616s.808,1.679,.808,2.735c0,.87-.187,1.616-.497,2.238s-.808,1.181-1.368,1.554c-.56,.373-1.243,.684-1.989,.87-.746,.187-1.492,.311-2.238,.311H2V7.203H10.144Zm-.497,6.963c.684,0,1.243-.187,1.679-.497s.622-.87,.622-1.554c0-.373-.062-.746-.187-.995s-.311-.435-.56-.622c-.249-.124-.497-.249-.808-.311s-.622-.062-.995-.062h-3.606v4.041h3.854Zm.187,7.336c.373,0,.746-.062,1.057-.124s.622-.187,.87-.373c.249-.187,.435-.373,.622-.684,.124-.311,.249-.684,.249-1.119,0-.87-.249-1.492-.746-1.927-.497-.373-1.181-.56-1.989-.56H5.792v4.787h4.041Zm11.998-.062c.497,.497,1.243,.746,2.238,.746,.684,0,1.306-.187,1.803-.497,.497-.373,.808-.746,.933-1.119h3.046c-.497,1.492-1.243,2.549-2.238,3.233-.995,.622-2.176,.995-3.606,.995-.995,0-1.865-.187-2.673-.497s-1.43-.746-1.989-1.368c-.56-.56-.995-1.243-1.243-2.052-.311-.808-.435-1.679-.435-2.673,0-.933,.124-1.803,.435-2.611s.746-1.492,1.306-2.114c.56-.56,1.243-1.057,1.989-1.368,.808-.311,1.616-.497,2.611-.497,1.057,0,1.989,.187,2.798,.622s1.43,.933,1.927,1.679c.497,.684,.87,1.492,1.119,2.362,.124,.87,.187,1.741,.124,2.735h-9.014c0,.995,.373,1.927,.87,2.425Zm3.917-6.528c-.435-.435-1.119-.684-1.927-.684-.56,0-.995,.124-1.368,.311-.373,.187-.622,.435-.87,.684-.249,.249-.373,.56-.435,.87s-.124,.56-.124,.808h5.595c-.124-.933-.435-1.554-.87-1.989Zm-5.471-6.528h6.963v1.679h-6.963v-1.679Z"></path></g>
              </svg>
              <span class="text-lg font-medium text-neutral-800 dark:text-neutral-200">Behance</span>
            </div>
          </div>

          <!-- Brand 4 -->
          <div class="flex min-w-[150px] items-center justify-center rounded-xl border border-black/5 bg-neutral-50 px-6 py-4 shadow-xs transition-all hover:bg-neutral-100 dark:border-white/10 dark:bg-neutral-800 dark:hover:bg-neutral-700">
            <div class="flex items-center space-x-3">
              <svg xmlns="http://www.w3.org/2000/svg" class="size-6" width="32" height="32" viewBox="0 0 32 32">
                <g fill="currentColor">
                  <path d="M3,3H15.381V15.381H3V3Z"></path>
                  <path d="M16.619,3h12.381V15.381h-12.381V3Z"></path>
                  <path d="M3,16.619H15.381v12.381H3v-12.381Z"></path>
                  <path d="M16.619,16.619h12.381v12.381h-12.381v-12.381Z"></path>
                </g>
              </svg>
              <span class="text-lg font-medium text-neutral-800 dark:text-neutral-200">Microsoft</span>
            </div>
          </div>

          <!-- Brand 5 -->
          <div class="flex min-w-[150px] items-center justify-center rounded-xl border border-black/5 bg-neutral-50 px-6 py-4 shadow-xs transition-all hover:bg-neutral-100 dark:border-white/10 dark:bg-neutral-800 dark:hover:bg-neutral-700">
            <div class="flex items-center space-x-3">
              <svg xmlns="http://www.w3.org/2000/svg" class="size-6" width="32" height="32" viewBox="0 0 32 32">
                <g fill="currentColor">
                  <path d="M8.463,19.43c0,1.503-1.228,2.731-2.731,2.731s-2.731-1.228-2.731-2.731,1.228-2.731,2.731-2.731h2.731v2.731Z"></path>
                  <path d="M9.839,19.43c0-1.503,1.228-2.731,2.731-2.731s2.731,1.228,2.731,2.731v6.839c0,1.503-1.228,2.731-2.731,2.731s-2.731-1.228-2.731-2.731c0,0,0-6.839,0-6.839Z"></path>
                  <path d="M12.57,8.463c-1.503,0-2.731-1.228-2.731-2.731s1.228-2.731,2.731-2.731,2.731,1.228,2.731,2.731v2.731h-2.731Z"></path>
                  <path d="M12.57,9.839c1.503,0,2.731,1.228,2.731,2.731s-1.228,2.731-2.731,2.731H5.731c-1.503,0-2.731-1.228-2.731-2.731s1.228-2.731,2.731-2.731c0,0,6.839,0,6.839,0Z"></path>
                  <path d="M23.537,12.57c0-1.503,1.228-2.731,2.731-2.731s2.731,1.228,2.731,2.731-1.228,2.731-2.731,2.731h-2.731v-2.731Z"></path>
                  <path d="M22.161,12.57c0,1.503-1.228,2.731-2.731,2.731s-2.731-1.228-2.731-2.731V5.731c0-1.503,1.228-2.731,2.731-2.731s2.731,1.228,2.731,2.731v6.839Z"></path>
                  <path d="M19.43,23.537c1.503,0,2.731,1.228,2.731,2.731s-1.228,2.731-2.731,2.731-2.731-1.228-2.731-2.731v-2.731h2.731Z"></path>
                  <path d="M19.43,22.161c-1.503,0-2.731-1.228-2.731-2.731s1.228-2.731,2.731-2.731h6.839c1.503,0,2.731,1.228,2.731,2.731s-1.228,2.731-2.731,2.731h-6.839Z"></path>
                </g>
              </svg>
              <span class="text-lg font-medium text-neutral-800 dark:text-neutral-200">Slack</span>
            </div>
          </div>
        </div>
      </div>
      <div class="pointer-events-none absolute inset-0 flex items-center justify-between">
        <div class="relative h-full w-full">
          <!-- Left gradient do define depending your page background color -->
          <div class="absolute inset-y-0 left-0 w-5 bg-gradient-to-r from-white to-transparent dark:from-[#080808]"></div>
          <!-- Right gradient do define depending your page background color -->
          <div class="absolute inset-y-0 right-0 w-5 bg-gradient-to-l from-white to-transparent dark:from-[#080808]"></div>
        </div>
      </div>
    </div>

    <!-- Second Row - Right Direction -->
    <div data-controller="marquee" data-marquee-hover-speed-value="0" data-marquee-speed-value="30" data-marquee-direction-value="right" class="relative overflow-hidden" data-action="mouseenter->marquee#pauseAnimation mouseleave->marquee#resumeAnimation">
      <!-- Marquee Track -->
      <div data-marquee-target="track" class="relative flex w-full">
        <!-- Marquee List -->
        <div data-marquee-target="list" class="flex shrink-0 flex-nowrap items-center gap-6 px-3 py-1">
          <!-- Brand 6 -->
          <div class="flex min-w-[150px] items-center justify-center rounded-xl border border-black/5 bg-neutral-50 px-6 py-4 shadow-xs transition-all hover:bg-neutral-100 dark:border-white/10 dark:bg-neutral-800 dark:hover:bg-neutral-700">
            <div class="flex items-center space-x-3">
              <svg xmlns="http://www.w3.org/2000/svg" class="size-6" width="32" height="32" viewBox="0 0 32 32">
                <g fill="currentColor"><path d="M28.783,10.412c-.005-3.575-2.789-6.505-6.056-7.563-4.057-1.313-9.407-1.123-13.281,.705C4.75,5.77,3.275,10.623,3.22,15.464c-.045,3.98,.352,14.461,6.264,14.536,4.393,.056,5.047-5.605,7.08-8.331,1.446-1.94,3.308-2.487,5.6-3.055,3.939-.975,6.624-4.084,6.619-8.203Z"></path></g>
              </svg>
              <span class="text-lg font-medium text-neutral-800 dark:text-neutral-200">Patreon</span>
            </div>
          </div>

          <!-- Brand 7 -->
          <div class="flex min-w-[150px] items-center justify-center rounded-xl border border-black/5 bg-neutral-50 px-6 py-4 shadow-xs transition-all hover:bg-neutral-100 dark:border-white/10 dark:bg-neutral-800 dark:hover:bg-neutral-700">
            <div class="flex items-center space-x-3">
              <svg xmlns="http://www.w3.org/2000/svg" class="size-6" width="32" height="32" viewBox="0 0 32 32">
                <g fill="currentColor"><path d="M16,2C8.268,2,2,8.268,2,16c0,5.931,3.69,11.001,8.898,13.041-.122-1.108-.233-2.811,.049-4.02,.254-1.093,1.642-6.959,1.642-6.959,0,0-.419-.839-.419-2.079,0-1.947,1.128-3.4,2.533-3.4,1.194,0,1.771,.897,1.771,1.972,0,1.201-.765,2.997-1.16,4.661-.33,1.393,.699,2.53,2.073,2.53,2.488,0,4.401-2.624,4.401-6.411,0-3.352-2.409-5.696-5.848-5.696-3.983,0-6.322,2.988-6.322,6.076,0,1.203,.464,2.494,1.042,3.195,.114,.139,.131,.26,.097,.402-.106,.442-.342,1.393-.389,1.588-.061,.256-.203,.311-.468,.187-1.749-.814-2.842-3.37-2.842-5.424,0-4.416,3.209-8.472,9.25-8.472,4.857,0,8.631,3.461,8.631,8.086,0,4.825-3.042,8.708-7.265,8.708-1.419,0-2.752-.737-3.209-1.608,0,0-.702,2.673-.872,3.328-.316,1.216-1.169,2.74-1.74,3.67,1.31,.406,2.702,.624,4.145,.624,7.732,0,14-6.268,14-14S23.732,2,16,2Z"></path></g>
              </svg>
              <span class="text-lg font-medium text-neutral-800 dark:text-neutral-200">Pinterest</span>
            </div>
          </div>

          <!-- Brand 8 -->
          <div class="flex min-w-[150px] items-center justify-center rounded-xl border border-black/5 bg-neutral-50 px-6 py-4 shadow-xs transition-all hover:bg-neutral-100 dark:border-white/10 dark:bg-neutral-800 dark:hover:bg-neutral-700">
            <div class="flex items-center space-x-3">
              <svg xmlns="http://www.w3.org/2000/svg" class="size-6" width="32" height="32" viewBox="0 0 32 32">
                <g fill="currentColor"><path d="M31.331,8.248c-.368-1.386-1.452-2.477-2.829-2.848-2.496-.673-12.502-.673-12.502-.673,0,0-10.007,0-12.502,.673-1.377,.37-2.461,1.462-2.829,2.848-.669,2.512-.669,7.752-.669,7.752,0,0,0,5.241,.669,7.752,.368,1.386,1.452,2.477,2.829,2.847,2.496,.673,12.502,.673,12.502,.673,0,0,10.007,0,12.502-.673,1.377-.37,2.461-1.462,2.829-2.847,.669-2.512,.669-7.752,.669-7.752,0,0,0-5.24-.669-7.752ZM12.727,20.758V11.242l8.364,4.758-8.364,4.758Z"></path></g>
              </svg>
              <span class="text-lg font-medium text-neutral-800 dark:text-neutral-200">YouTube</span>
            </div>
          </div>

          <!-- Brand 9 -->
          <div class="flex min-w-[150px] items-center justify-center rounded-xl border border-black/5 bg-neutral-50 px-6 py-4 shadow-xs transition-all hover:bg-neutral-100 dark:border-white/10 dark:bg-neutral-800 dark:hover:bg-neutral-700">
            <div class="flex items-center space-x-3">
              <svg xmlns="http://www.w3.org/2000/svg" class="size-6" width="24" height="24" viewBox="0 0 24 24">
                <g fill="none">
                  <path d="M5.55502 3.61569C5.12664 3.18731 4.91245 2.97312 4.89798 2.78923C4.88542 2.62967 4.95001 2.47374 5.07171 2.3698C5.21198 2.25 5.51489 2.25 6.1207 2.25H16.35C17.1901 2.25 17.6101 2.25 17.931 2.41349C18.2132 2.5573 18.4427 2.78677 18.5865 3.06901C18.75 3.38988 18.75 3.80992 18.75 4.65V7.35C18.75 8.19008 18.75 8.61012 18.5865 8.93099C18.4427 9.21323 18.2132 9.4427 17.931 9.58651C17.6101 9.75 17.1901 9.75 16.35 9.75H15.2249L14.1308 8.65589C13.6119 8.13703 13.3525 7.87761 13.0497 7.69208C12.7813 7.52759 12.4887 7.40638 12.1826 7.33289C11.8373 7.25 11.4704 7.25 10.7366 7.25H9.18933L5.55502 3.61569Z" fill="currentColor"></path>
                  <path d="M11.3843 21.445C11.8127 21.8734 12.0269 22.0875 12.2108 22.102C12.3703 22.1146 12.5262 22.05 12.6302 21.9283C12.75 21.788 12.75 21.4851 12.75 20.8793V16.75H6.68933L11.3843 21.445Z" fill="currentColor"></path>
                  <path d="M5.25 10.65C5.25 9.80992 5.25 9.38988 5.41349 9.06901C5.5573 8.78677 5.78677 8.5573 6.06901 8.41349C6.38988 8.25 6.80992 8.25 7.65 8.25H10.9852C11.4744 8.25 11.7189 8.25 11.9491 8.30526C12.1532 8.35425 12.3483 8.43506 12.5272 8.54472C12.7291 8.6684 12.902 8.84136 13.2479 9.18726L18.445 14.3843C18.8734 14.8127 19.0875 15.0269 19.102 15.2108C19.1146 15.3703 19.05 15.5263 18.9283 15.6302C18.788 15.75 18.4851 15.75 17.8793 15.75H5.68934V15.75C5.40804 15.4687 5.25 15.0872 5.25 14.6893V10.65Z" fill="currentColor"></path>
                </g>
              </svg>
              <span class="text-lg font-medium text-neutral-800 dark:text-neutral-200">Framer</span>
            </div>
          </div>

          <!-- Brand 11 -->
          <div class="flex min-w-[150px] items-center justify-center rounded-xl border border-black/5 bg-neutral-50 px-6 py-4 shadow-xs transition-all hover:bg-neutral-100 dark:border-white/10 dark:bg-neutral-800 dark:hover:bg-neutral-700">
            <div class="flex items-center space-x-3">
              <svg xmlns="http://www.w3.org/2000/svg" class="size-6" width="24" height="24" viewBox="0 0 24 24">
                <g fill="none"><path fill-rule="evenodd" clip-rule="evenodd" d="M12 1.25C6.06294 1.25 1.25 6.06294 1.25 12C1.25 17.9371 6.06294 22.75 12 22.75C17.9371 22.75 22.75 17.9371 22.75 12C22.75 6.06294 17.9371 1.25 12 1.25ZM7.24064 10.243C6.84845 10.3756 6.42251 10.1654 6.28965 9.77326C6.15675 9.38095 6.36704 8.95518 6.75936 8.82227L6.76064 8.82184C7.11822 8.70159 7.48582 8.61304 7.85391 8.53176C8.52557 8.38345 9.47301 8.22053 10.5843 8.1602C12.7986 8.03998 15.7214 8.32449 18.3991 10.0073C18.7498 10.2277 18.8554 10.6906 18.635 11.0413C18.4146 11.3921 17.9516 11.4977 17.6009 11.2773C15.2786 9.81778 12.7014 9.54747 10.6657 9.65799C9.65199 9.71302 8.78693 9.86187 8.17734 9.99648C7.86261 10.066 7.54712 10.1423 7.24064 10.243ZM7.24011 11.7965L7.23879 11.7969C6.85051 11.9412 6.65269 12.3729 6.79696 12.7612C6.94116 13.1493 7.37306 13.3469 7.76121 13.203C8.01105 13.1136 8.26893 13.0456 8.52676 12.9836C9.03189 12.8623 9.75307 12.7275 10.6068 12.677C12.318 12.5758 14.5214 12.8144 16.5979 14.1331C16.9476 14.3552 17.4111 14.2517 17.6331 13.9021C17.8552 13.5524 17.7517 13.0889 17.4021 12.8669C14.9786 11.3278 12.432 11.0664 10.5182 11.1797C9.55943 11.2364 8.74936 11.3875 8.17636 11.5251C7.86123 11.6008 7.54651 11.6843 7.2419 11.7958L7.24011 11.7965ZM8.24708 16.1038C7.85613 16.24 7.42827 16.0337 7.29187 15.6428C7.15541 15.2517 7.36183 14.824 7.75292 14.6876C8.60177 14.3947 9.50664 14.2708 10.4004 14.2265C11.9342 14.1505 13.9791 14.3679 15.9019 15.5885C16.2516 15.8105 16.3552 16.2739 16.1332 16.6236C15.9112 16.9733 15.4478 17.0769 15.0981 16.8549C13.5209 15.8538 11.8158 15.6582 10.4746 15.7246C9.72314 15.7619 8.96309 15.8652 8.24708 16.1038Z" fill="currentColor"></path></g>
              </svg>
              <span class="text-lg font-medium text-neutral-800 dark:text-neutral-200">Spotify</span>
            </div>
          </div>

          <!-- Brand 12 -->
          <div class="flex min-w-[150px] items-center justify-center rounded-xl border border-black/5 bg-neutral-50 px-6 py-4 shadow-xs transition-all hover:bg-neutral-100 dark:border-white/10 dark:bg-neutral-800 dark:hover:bg-neutral-700">
            <div class="flex items-center space-x-3">
              <svg xmlns="http://www.w3.org/2000/svg" class="size-6" width="32" height="32" viewBox="0 0 32 32">
                <g fill="currentColor"><path d="M26.413,6.536c-1.971-.902-4.052-1.543-6.189-1.904-.292,.523-.557,1.061-.793,1.612-2.277-.343-4.592-.343-6.869,0-.236-.551-.5-1.089-.793-1.612-2.139,.365-4.221,1.006-6.194,1.909C1.658,12.336,.596,17.987,1.127,23.558h0c2.294,1.695,4.861,2.984,7.591,3.811,.615-.827,1.158-1.704,1.626-2.622-.888-.332-1.744-.741-2.56-1.222,.215-.156,.425-.316,.628-.472,4.806,2.26,10.37,2.26,15.177,0,.205,.168,.415,.328,.628,.472-.817,.483-1.676,.892-2.565,1.225,.467,.918,1.011,1.794,1.626,2.619,2.732-.824,5.301-2.112,7.596-3.808h0c.623-6.461-1.064-12.06-4.46-17.025Zm-15.396,13.596c-1.479,0-2.702-1.343-2.702-2.994s1.18-3.006,2.697-3.006,2.73,1.354,2.704,3.006-1.192,2.994-2.699,2.994Zm9.967,0c-1.482,0-2.699-1.343-2.699-2.994s1.18-3.006,2.699-3.006,2.723,1.354,2.697,3.006-1.189,2.994-2.697,2.994Z"></path></g>
              </svg>
              <span class="text-lg font-medium text-neutral-800 dark:text-neutral-200">Discord</span>
            </div>
          </div>
        </div>
      </div>
      <div class="pointer-events-none absolute inset-0 flex items-center justify-between">
        <div class="relative h-full w-full">
          <!-- Left gradient do define depending your page background color -->
          <div class="absolute inset-y-0 left-0 w-5 bg-gradient-to-r from-white to-transparent dark:from-[#080808]"></div>
          <!-- Right gradient do define depending your page background color -->
          <div class="absolute inset-y-0 right-0 w-5 bg-gradient-to-l from-white to-transparent dark:from-[#080808]"></div>
        </div>
      </div>
    </div>
  </div>
</div>

Slow on Hover Marquee

Instead of pausing on hover, this marquee slows down by setting hoverSpeed to a higher value (40s), creating a smooth reading experience while the animation.

Ruby on Rails
Stimulus
Tailwind CSS
Javascript
<div class="w-full">
  <div
    data-controller="marquee"
    data-marquee-speed-value="15"
    data-marquee-hover-speed-value="40"
    data-marquee-direction-value="left"
    class="relative overflow-hidden bg-transparent py-4"
    data-action="mouseenter->marquee#pauseAnimation mouseleave->marquee#resumeAnimation"
  >
    <div data-marquee-target="track" class="relative flex w-full">
      <div data-marquee-target="list" class="flex w-full shrink-0 flex-nowrap items-center justify-around gap-8 px-4">
        <div class="flex items-center space-x-2">
          <svg xmlns="http://www.w3.org/2000/svg" fill="#d30001" viewBox="0 0 235 155" class="h-5">
            <g>
              <path d="M180 17.8c-.9-.5-3.4-1.7-9.7-3.5l-.4 6.6c3.3 1.1 6.5 2.3 9.7 3.6l.4-6.7Z"></path>
              <path d="M124.9 18.9c-29.2 2.6-65 29.1-86 64.1S15 147.6 15 147.6h79s-15.1-68.9 34.9-96.8c10.9-5.3 45.6-25.1 102.4 16.9 1.8-1.5 3.5-2.7 3.5-2.7s-52-51.9-109.9-46.1Z"></path>
              <path d="m54.7 54.7-7.1-6.2c-2.6 2.5-5.1 5-7.4 7.5l7.7 6.6c2.1-2.7 4.4-5.4 6.8-7.9Z"></path>
              <path d="M92.5 25 88 18.1c-2.5 1.3-5.1 2.7-7.8 4.3l4.6 7c2.6-1.6 5.1-3.1 7.7-4.4Z"></path>
              <path d="M24.5 99.4 13 95.2c-1.9 4.3-4 9.3-5 12l11.5 4.2c1.3-3.4 3.4-8.3 5-12Z"></path>
              <path d="M133.1 14.5h1l-2-6.1c-3.1 0-6.3.2-9.6.6l1.9 5.9c2.9-.3 5.8-.4 8.7-.4Z"></path>
              <path d="M97 127.6c.2 5.3.7 9.6 1.2 12.6l12 4.3c-.9-3.9-1.8-8.3-2.4-13L97 127.6Z"></path>
              <path d="m137.9 51.3 2.3 6.9c2.9-1.4 5.8-2.6 8.7-3.5l-2.2-6.6c-3.4 1-6.3 2.1-8.8 3.2Z"></path>
              <path d="M169.7 52c3.3.1 6.6.5 9.9 1.2l.4-6.2c-3.4-.7-6.7-1.1-9.9-1.3l-.4 6.3Z"></path>
              <path d="M101.5 91.2c-1.3 3.7-2.2 7.4-3 11l8.1 6.4c.4-3.9 1.1-7.8 2.1-11.7l-7.2-5.7Z"></path>
              <path d="m113 70 4.8 7.2c1.7-2.5 3.7-4.8 5.9-7.1l-4.5-6.8c-2.3 2.1-4.4 4.4-6.2 6.7Z"></path>
            </g>
          </svg>
          <span class="text-sm font-medium text-neutral-800 dark:text-neutral-200">Ruby on Rails</span>
        </div>
        <div class="flex items-center space-x-2">
          <svg class="size-4 rounded-sm" width="60" height="60" viewBox="0 0 60 60" xmlns="http://www.w3.org/2000/svg">
            <g class="fill-neutral-800 dark:fill-white">
              <path d="M1.76 1.85885e-08H58.2399C58.7016 -6.70275e-05 59.1447 0.181237 59.474 0.50484C59.8032 0.828443 59.9921 1.26844 59.9999 1.73V7.37823C59.9999 7.87239 59.9999 8.11947 59.9038 8.30822C59.8192 8.47424 59.6842 8.60923 59.5182 8.69382C59.3294 8.78999 59.0823 8.78999 58.5882 8.78999H45.92C44.7722 8.83556 43.6502 9.14343 42.64 9.68999L15.64 25.55C14.6261 26.0964 13.5008 26.4042 12.35 26.45H1.41176C0.9176 26.45 0.670518 26.45 0.481773 26.3538C0.315747 26.2692 0.180765 26.1342 0.0961706 25.9682C0 25.7795 0 25.5324 0 25.0382V17.2717C0 16.7776 0 16.5305 0.0961706 16.3418C0.180765 16.1757 0.315747 16.0408 0.481773 15.9562C0.670518 15.86 0.9176 15.86 1.41176 15.86H12.35C13.5001 15.9106 14.6243 16.2181 15.64 16.76L18.64 18.51C19.1037 18.7494 19.6181 18.8743 20.14 18.8743C20.6619 18.8743 21.1762 18.7494 21.64 18.51L25.48 16.25C25.6438 16.1661 25.7812 16.0386 25.8772 15.8815C25.9732 15.7245 26.024 15.544 26.024 15.36C26.024 15.1759 25.9732 14.9955 25.8772 14.8384C25.7812 14.6814 25.6438 14.5539 25.48 14.47L17.4 9.71999C16.3879 9.17798 15.2669 8.8704 14.12 8.81999H1.41176C0.9176 8.81999 0.670518 8.81999 0.481773 8.72382C0.315747 8.63923 0.180765 8.50424 0.0961706 8.33822C0 8.14947 0 7.90239 0 7.40823V1.76C0 1.29322 0.185428 0.845555 0.515492 0.515492C0.845555 0.185428 1.29322 1.85885e-08 1.76 1.85885e-08Z"></path>
              <path d="M47.65 15.8799C46.4998 15.9305 45.3757 16.238 44.36 16.7799L17.36 32.6499C16.3479 33.1919 15.2269 33.4994 14.08 33.5499H1.41176C0.9176 33.5499 0.670518 33.5499 0.481773 33.646C0.315747 33.7306 0.180765 33.8656 0.0961706 34.0316C0 34.2204 0 34.4674 0 34.9616V42.7281C0 43.2222 0 43.4693 0.0961706 43.6581C0.180765 43.8241 0.315747 43.9591 0.481773 44.0437C0.670518 44.1398 0.9176 44.1398 1.41176 44.1398H12.35C13.5001 44.0892 14.6243 43.7817 15.64 43.2398L42.64 27.3699C43.652 26.8278 44.773 26.5203 45.92 26.4699H58.5882C59.0823 26.4699 59.3294 26.4699 59.5182 26.3737C59.6842 26.2891 59.8192 26.1541 59.9038 25.9881C59.9999 25.7993 59.9999 25.5523 59.9999 25.0581V17.2916C59.9999 16.7975 59.9999 16.5504 59.9038 16.3616C59.8192 16.1956 59.6842 16.0606 59.5182 15.976C59.3294 15.8799 59.0823 15.8799 58.5882 15.8799H47.65Z"></path>
              <path d="M47.65 33.56C46.4998 33.6106 45.3757 33.9182 44.36 34.46L17.36 50.32C16.3497 50.8666 15.2277 51.1744 14.08 51.22H1.41176C0.9176 51.22 0.670518 51.22 0.481773 51.3162C0.315747 51.4008 0.180765 51.5358 0.0961706 51.7018C0 51.8905 0 52.1376 0 52.6318V58.28C0.0129646 58.739 0.203789 59.175 0.532182 59.4959C0.860576 59.8168 1.30083 59.9976 1.76 60H58.2399C58.7042 59.9974 59.1488 59.8125 59.4781 59.4852C59.8073 59.1578 59.9947 58.7142 59.9999 58.25V52.6018C59.9999 52.1076 59.9999 51.8605 59.9038 51.6718C59.8192 51.5058 59.6842 51.3708 59.5182 51.2862C59.3294 51.19 59.0823 51.19 58.5882 51.19H45.88C44.7322 51.1444 43.6102 50.8366 42.6 50.29L34.52 45.55C34.3536 45.4671 34.2136 45.3394 34.1157 45.1813C34.0179 45.0232 33.966 44.8409 33.966 44.655C33.966 44.4691 34.0179 44.2868 34.1157 44.1287C34.2136 43.9706 34.3536 43.843 34.52 43.76L38.36 41.51C38.8237 41.2706 39.3381 41.1457 39.86 41.1457C40.3819 41.1457 40.8962 41.2706 41.36 41.51L44.36 43.25C45.3739 43.7964 46.4991 44.1042 47.65 44.15H58.5882C59.0823 44.15 59.3294 44.15 59.5182 44.0538C59.6842 43.9693 59.8192 43.8343 59.9038 43.6682C59.9999 43.4795 59.9999 43.2324 59.9999 42.7383V34.9718C59.9999 34.4776 59.9999 34.2305 59.9038 34.0418C59.8192 33.8758 59.6842 33.7408 59.5182 33.6562C59.3294 33.56 59.0823 33.56 58.5882 33.56H47.65Z"></path>
            </g>
          </svg>
          <span class="text-sm font-medium text-neutral-800 dark:text-neutral-200">Stimulus</span>
        </div>
        <div class="flex items-center space-x-2">
          <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20" class="h-5"><path fill="#23B2E7" fill-rule="evenodd" d="M10 5Q6 5 5 8.334q1.5-1.668 3.5-1.25c.76.158 1.305.618 1.906 1.127C11.386 9.041 12.522 10 15 10q4 0 5-3.333-1.5 1.667-3.5 1.25c-.76-.159-1.305-.619-1.906-1.128C13.614 5.96 12.479 5 10 5m-5 5q-4 0-5 3.334 1.5-1.668 3.5-1.25c.76.158 1.305.618 1.906 1.127C6.386 14.041 7.521 15 10 15q4 0 5-3.333-1.5 1.667-3.5 1.25c-.76-.159-1.305-.619-1.906-1.128C8.614 10.96 7.478 10 5 10" clip-rule="evenodd"></path></svg>
          <span class="text-sm font-medium text-neutral-800 dark:text-neutral-200">Tailwind CSS</span>
        </div>
        <div class="flex items-center space-x-2">
          <svg xmlns="http://www.w3.org/2000/svg" class="size-4 rounded-sm" viewBox="0 0 630 630">
            <rect width="630" height="630" fill="#f7df1e"/>
            <path d="m423.2 492.19c12.69 20.72 29.2 35.95 58.4 35.95 24.53 0 40.2-12.26 40.2-29.2 0-20.3-16.1-27.49-43.1-39.3l-14.8-6.35c-42.72-18.2-71.1-41-71.1-89.2 0-44.4 33.83-78.2 86.7-78.2 37.64 0 64.7 13.1 84.2 47.4l-46.1 29.6c-10.15-18.2-21.1-25.37-38.1-25.37-17.34 0-28.33 11-28.33 25.37 0 17.76 11 24.95 36.4 35.95l14.8 6.34c50.3 21.57 78.7 43.56 78.7 93 0 53.3-41.87 82.5-98.1 82.5-54.98 0-90.5-26.2-107.88-60.54zm-209.13 5.13c9.3 16.5 17.76 30.45 38.1 30.45 19.45 0 31.72-7.61 31.72-37.2v-201.3h59.2v202.1c0 61.3-35.94 89.2-88.4 89.2-47.4 0-74.85-24.53-88.81-54.075z"/>
          </svg>
          <span class="text-sm font-medium text-neutral-800 dark:text-neutral-200">Javascript</span>
        </div>
      </div>
    </div>
    <div class="flex pointer-events-none absolute inset-0 justify-between items-center">
      <div class="relative h-full w-full">
        <!-- Left gradient do define depending your page background color -->
        <div class="w-5 bg-gradient-to-r from-white dark:from-[#080808] to-transparent absolute left-0 inset-y-0"></div>
        <!-- Right gradient do define depending your page background color -->
        <div class="w-5 bg-gradient-to-l from-white dark:from-[#080808] to-transparent absolute right-0 inset-y-0"></div>
      </div>
    </div>
  </div>
</div>

Configuration

The marquee component is powered by a Stimulus controller that provides smooth infinite scrolling animations with hover controls and customizable speed.

Controller Setup

Basic marquee structure with required data attributes:

<div data-controller="marquee"
     data-marquee-speed-value="20"
     data-marquee-hover-speed-value="0"
     data-marquee-direction-value="left"
     data-action="mouseenter->marquee#pauseAnimation mouseleave->marquee#resumeAnimation">
  <div data-marquee-target="track">
    <div data-marquee-target="list">
      <!-- Your content here -->
    </div>
  </div>
</div>

Configuration Values

Prop Description Type Default
speed
Animation duration in seconds (lower = faster) Number 20
hoverSpeed
Speed when hovered (0 = pause, higher = slower) Number 0
direction
Scroll direction: 'left' or 'right' String "left"
clones
Number of content clones for seamless loop Number 2

Targets

Target Description Required
track
The container that holds all scrolling content and clones Required
list
The original content container that gets cloned Required

Actions

Action Description Usage
pauseAnimation
Pauses or slows down the marquee animation data-action="mouseenter->marquee#pauseAnimation"
resumeAnimation
Resumes normal speed after pause/slow data-action="mouseleave->marquee#resumeAnimation"

Key Features

  • Infinite Scrolling: Seamlessly loops content using clones
  • Hover Control: Pause on hover or slow down for better readability
  • Bi-directional: Supports both left and right scrolling directions
  • Performance Optimized: Uses CSS animations with transform for smooth 60fps scrolling
  • Responsive: Automatically adjusts to content size changes
  • Smooth Speed Changes: Transitions smoothly between different speeds

Usage Notes

  • Speed values are in seconds - lower values mean faster animation
  • Set hoverSpeed to 0 to completely pause on hover
  • Set hoverSpeed to a value higher than speed to slow down on hover
  • The controller automatically handles content cloning for seamless loops
  • Add fade gradients on the sides to create a smooth visual effect
  • Content should have flex-shrink: 0 to prevent compression

Table of contents

Get notified when new components come out