Infinite Scroll Animation

React & Next js
/Components/InfiniteScroller.tsx

"use client";
import { useEffect } from "react";

const InfiniteScroll = () => {
  useEffect(() => {
    const scrollers = document.querySelectorAll(".scroller");
    if (!window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
      addAnimation();
    }
    function addAnimation() {
      scrollers.forEach((scroller) => {
        scroller.setAttribute("data-animated", "true");
      });
      const srcollerInner = document.querySelector(
        ".scroller__inner"
      ) as HTMLElement;
      if (srcollerInner) {
        const scrollerContent = Array.from(srcollerInner.children);
        scrollerContent.forEach((item) => {
          const duplicatedItem = item.cloneNode(true) as HTMLElement;
          duplicatedItem.setAttribute("aria-hidden", "true");
          srcollerInner.appendChild(duplicatedItem);
        });
      }
    }
  }, []);

  return (
    <div className="flex flex-col gap-4 items-center justify-center font-inter font-mono py-5">
      <h1 className="text-4xl font-bold text-white text-center">
        Infinite Scroll Animation
      </h1>
      <div className="scroller" data-direction="right">
        <ul className="tag-list scroller__inner">
          <li>HTML</li>
          <li>CSS</li>
          <li>JS</li>
          <li>Angular</li>
          <li>React</li>
          <li>Webdev</li>
          <li>Animation</li>
          <li>UI/UX</li>
          <li>Node</li>
        </ul>
      </div>
    </div>
  );
};

export default InfiniteScroll;
/globals.css

.scroller {
  max-width: 600px;
}
  
.scroller__inner {
  padding-block: 1rem;
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}

.scroller[data-animated="true"] {
  overflow: hidden;
  -webkit-mask: linear-gradient(
    90deg,
    transparent,
    white 20%,
    white 80%,
    transparent
  );
  mask: linear-gradient(90deg, transparent, white 20%, white 80%, transparent);
}

.scroller[data-animated="true"] .scroller__inner {
  width: max-content;
  flex-wrap: nowrap;
  animation: scroll var(--_animation-duration, 40s)
    var(--_animation-direction, forwards) linear infinite;
}

.scroller[data-animated="true"] .scroller__inner:hover {
  animation-play-state: paused;
}

.scroller[data-direction="right"] {
  --_animation-direction: reverse;
}

.scroller[data-direction="left"] {
  --_animation-direction: forwards;
}

.scroller[data-speed="fast"] {
  --_animation-duration: 20s;
}

.scroller[data-speed="slow"] {
  --_animation-duration: 60s;
}

@keyframes scroll {
  to {
    transform: translate(calc(-50% - 0.5rem));
  }
}

.tag-list {
  margin: 0;
  padding-inline: 0;
  list-style: none;
}

.tag-list li {
  padding: 1rem;
  background: #d2d2d2;
  border-radius: 0.5rem;
  box-shadow: 0 0.5rem 1rem -0.25rem #404040;
}