<h1 style="text-align: center">Infinite Scroll Animation</h1>
<div class="main-container">
<div class="scroller" data-speed="slow">
<ul class="tag-list scroller__inner">
<li>HTML</li>
<li>CSS</li>
<li>JS</li>
<li>SSG</li>
<li>Design</li>
<li>Animation</li>
<li>UI/UX</li>
</ul>
</div>
<div class="scroller" data-speed="fast" data-direction="up">
<ul class="tag-list scroller__inner">
<li>HTML</li>
<li>CSS</li>
<li>JS</li>
<li>SSG</li>
<li>Design</li>
<li>Animation</li>
<li>UI/UX</li>
</ul>
</div>
</div>
/* general styles */
:root {
--clr-neutral-100: hsl(0, 0%, 100%);
--clr-primary-100: hsl(205, 15%, 58%);
--clr-primary-400: hsl(215, 25%, 27%);
--clr-primary-800: hsl(217, 33%, 17%);
--clr-primary-900: hsl(218, 33%, 9%);
--gap: 1rem;
}
html {
color-scheme: dark;
}
body {
display: grid;
min-block-size: 100vh;
place-content: center;
font-family: system-ui;
font-size: 1.125rem;
background-color: var(--clr-primary-800);
}
.main-container {
display: flex;
gap: 24px;
height: 400px;
}
/* elemets style */
.scroller {
width: 400px;
height: 100%;
will-change: transform;
}
.tag-list {
margin: 0;
padding-inline: 0;
margin: 0 auto;
list-style: none;
}
.tag-list li {
padding: 1.2rem;
background: var(--clr-primary-400);
margin-bottom: var(--gap);
border-radius: 0.25rem;
box-shadow: 0 0.5rem 1rem -0.25rem var(--clr-primary-900);
}
.scroller__inner {
width: 100%;
white-space: nowrap;
will-change: transform;
gap: var(--gap);
}
/* edges mask */
.scroller[data-animated="true"] {
overflow: hidden;
-webkit-mask: linear-gradient(
0deg,
transparent,
white 20%,
white 80%,
transparent
);
mask: linear-gradient(0deg, transparent, white 20%, white 80%, transparent);
}
.scroller[data-animated="true"] .scroller__inner {
animation: scroll var(--_animation-duration, 15s)
var(--_animation-direction, forwards) linear infinite;
}
/* direction */
.scroller[data-direction="up"] {
--_animation-direction: reverse;
}
.scroller[data-direction="down"] {
--_animation-direction: forwards;
}
/* speed */
.scroller[data-speed="fast"] {
--_animation-duration: 10s;
}
.scroller[data-speed="slow"] {
--_animation-duration: 24s;
}
/* animation */
@keyframes scroll {
from {
transform: translateY(0);
}
to {
transform: translateY(calc(-50% - var(--gap) / 2));
}
}
const scrollers = document.querySelectorAll(".scroller");
// If a user hasn't opted in for recuded motion, then we add the animation
if (!window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
addAnimation();
}
function addAnimation() {
scrollers.forEach((scroller) => {
// add data-animated="true" to every `.scroller` on the page
scroller.setAttribute("data-animated", true);
// Make an array from the elements within `.scroller-inner`
const scrollerInner = scroller.querySelector(".scroller__inner");
const scrollerContent = Array.from(scrollerInner.children);
// For each item in the array, clone it
// add aria-hidden to it
// add it into the `.scroller-inner`
scrollerContent.forEach((item) => {
const duplicatedItem = item.cloneNode(true);
duplicatedItem.setAttribute("aria-hidden", true);
scrollerInner.appendChild(duplicatedItem);
});
});
}
This Pen doesn't use any external JavaScript resources.