Infinite marquee
A seamless looping ticker of words, built by duplicating the track and translating it by exactly half.
CSSBeginner
<div class="marquee" aria-label="Design, Motion, Code, Ship"><div class="track" aria-hidden="true">
<span class="item">Design</span><span class="item">Motion</span><span class="item">Code</span><span class="item">Ship</span>
<span class="item">Design</span><span class="item">Motion</span><span class="item">Code</span><span class="item">Ship</span>
</div></div>
body{display:block;overflow:hidden}
.marquee{position:absolute;top:50%;left:0;right:0;transform:translateY(-50%);overflow:hidden;
-webkit-mask-image:linear-gradient(90deg,transparent,#000 10%,#000 90%,transparent);mask-image:linear-gradient(90deg,transparent,#000 10%,#000 90%,transparent)}
.track{display:flex;width:max-content;animation:scroll 18s linear infinite}
.marquee:hover .track{animation-play-state:paused}
.item{flex:none;padding:0 28px;font-size:clamp(2rem,8vw,3.4rem);font-weight:800;letter-spacing:-.03em;white-space:nowrap}
.item:nth-child(even){color:transparent;-webkit-text-stroke:1.5px #f08a5d}
@keyframes scroll{to{transform:translateX(-50%)}}
@media (prefers-reduced-motion:reduce){.track{animation:none}}
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Infinite marquee</title>
<style>
*{box-sizing:border-box}
html,body{margin:0;height:100%}
body{display:grid;place-items:center;background:#140807;color:#fffffc;font:16px/1.5 system-ui,-apple-system,"Segoe UI",sans-serif}
body{display:block;place-items:normal;overflow:hidden}
.marquee{position:absolute;top:50%;left:0;right:0;transform:translateY(-50%);overflow:hidden;
-webkit-mask-image:linear-gradient(90deg,transparent,#000 10%,#000 90%,transparent);mask-image:linear-gradient(90deg,transparent,#000 10%,#000 90%,transparent)}
.track{display:flex;width:max-content;animation:scroll 18s linear infinite}
.marquee:hover .track{animation-play-state:paused}
.item{flex:none;padding:0 28px;font-size:clamp(2rem,8vw,3.4rem);font-weight:800;letter-spacing:-.03em;white-space:nowrap}
.item:nth-child(even){color:transparent;-webkit-text-stroke:1.5px #f08a5d}
@keyframes scroll{to{transform:translateX(-50%)}}
@media (prefers-reduced-motion:reduce){.track{animation:none}}
</style></head>
<body>
<div class="marquee" aria-label="Design, Motion, Code, Ship"><div class="track" aria-hidden="true">
<span class="item">Design</span><span class="item">Motion</span><span class="item">Code</span><span class="item">Ship</span>
<span class="item">Design</span><span class="item">Motion</span><span class="item">Code</span><span class="item">Ship</span>
</div></div>
</body></html>
Good for
Logo walls, keyword bands and announcements. Pause on hover so it is readable.
Watch out for
The track must contain two identical copies and move exactly -50% to loop without a jump. Respect reduced motion.
Accessibility
Wrap motion in @media (prefers-reduced-motion: reduce) and switch it off or shorten it for people who ask for less movement.