SolutionsCase StudiesAI ToolsResourcesAboutContact
Get Your Free AI Automation Audit Book a Free 30-Minute Strategy Call
Playground~/frontend Open editor

~/css/marquees

7 CSS Infinite Marquees

4 Pure CSS3 with JSPublished 2026-10-05Free · no sign-up

Seven endless scrolling strips: a logo wall with faded edges, two rows moving in opposite directions, a vertical headline ticker, giant outlined words, an announcement bar with a real pause button, a tilted ribbon and a marquee that slows to a crawl when you point at it.

01. Logo Wall Marquee

MarqueeLogos

Client wordmarks scroll sideways in a seamless loop with faded edges. The list is written twice and the copy is aria-hidden, the track moves by exactly half its width so the loop has no jump, and hovering pauses it.

View code
<div class="mq1" aria-label="Brands we have worked with"><ul class="t"><li>Northwind</li><li>Lumen</li><li>Kiln &amp; Co</li><li>Brew Lab</li><li>Linea</li></ul><ul class="t" aria-hidden="true"><li>Northwind</li><li>Lumen</li><li>Kiln &amp; Co</li><li>Brew Lab</li><li>Linea</li></ul></div>
.mq1 { display: flex; width: 320px; max-width: 100%; overflow: hidden; -webkit-mask: linear-gradient(90deg, transparent, #000 15%, #000 85%, transparent); mask: linear-gradient(90deg, transparent, #000 15%, #000 85%, transparent); font-family: Inter, system-ui, sans-serif; }
.mq1 .t { display: flex; flex: none; gap: 34px; margin: 0; padding: 0 34px 0 0; list-style: none; animation: mq1 16s linear infinite; }
.mq1:hover .t { animation-play-state: paused; }
.mq1 li { color: #344054; font-size: 20px; font-weight: 800; letter-spacing: -.02em; white-space: nowrap; }
@keyframes mq1 { to { transform: translateX(-100%); } }
@media (prefers-reduced-motion: reduce) { .mq1 { flex-wrap: wrap; -webkit-mask: none; mask: none; } .mq1 .t { animation: none; flex-wrap: wrap; gap: 14px; } .mq1 .t[aria-hidden] { display: none; } }

02. Two Rows in Opposite Directions

MarqueeTags

Two rows of skill tags drift in opposite directions, a lively section divider. The second row reuses the same keyframes with animation-direction: reverse, and both rows share one hover pause on the container.

View code
<div class="mq2" aria-label="Skills"><div class="row"><ul><li>Shopify</li><li>React</li><li>CSS Grid</li><li>Accessibility</li></ul><ul aria-hidden="true"><li>Shopify</li><li>React</li><li>CSS Grid</li><li>Accessibility</li></ul></div><div class="row r" aria-hidden="true"><ul><li>Figma</li><li>Node</li><li>SEO</li><li>Analytics</li></ul><ul><li>Figma</li><li>Node</li><li>SEO</li><li>Analytics</li></ul></div><p class="sr">Also Figma, Node, SEO and analytics</p></div>
.mq2 { display: grid; gap: 10px; width: 320px; max-width: 100%; overflow: hidden; font-family: Inter, system-ui, sans-serif; }
.mq2 .row { display: flex; }
.mq2 ul { display: flex; flex: none; gap: 10px; margin: 0; padding: 0 10px 0 0; list-style: none; animation: mq2 14s linear infinite; }
.mq2 .r ul { animation-direction: reverse; }
.mq2:hover ul { animation-play-state: paused; }
.mq2 li { padding: 8px 14px; border: 1px solid #334155; border-radius: 999px; background: #1e293b; color: #e2e8f0; font-size: 14px; font-weight: 600; white-space: nowrap; }
.mq2 .r li { border-color: #4c1d95; background: #2e1065; color: #ddd6fe; }
.mq2 .sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
@keyframes mq2 { to { transform: translateX(-100%); } }
@media (prefers-reduced-motion: reduce) { .mq2 ul { animation: none; } }

03. Vertical News Ticker

MarqueeVertical

Headlines scroll upward in a fixed-height box, one line at a time with a pause on each, using stepped keyframes. It is a list of links, and a Pause button stops the motion, which WCAG requires for anything that moves for more than five seconds.

View code
<div class="mq3"><b>Latest</b><div class="vp"><ul><li><a href="#n1">New spring catalogue is live</a></li><li><a href="#n2">Free returns extended to 60 days</a></li><li><a href="#n3">Studio open day on 18 October</a></li><li aria-hidden="true"><a href="#n1" tabindex="-1">New spring catalogue is live</a></li></ul></div><button aria-pressed="false" aria-label="Pause headlines">❚❚</button></div>
.mq3 { display: flex; align-items: center; gap: 10px; width: 330px; max-width: 100%; padding: 8px 10px; box-sizing: border-box; border: 1px solid #e4e7ec; border-radius: 10px; background: #fff; font: 14px Inter, system-ui, sans-serif; }
.mq3 b { padding: 3px 8px; border-radius: 6px; background: #b42318; color: #fff; font-size: 12px; }
.mq3 .vp { flex: 1; min-width: 0; height: 22px; overflow: hidden; }
.mq3 ul { margin: 0; padding: 0; list-style: none; animation: mq3 9s infinite; }
.mq3 li { height: 22px; line-height: 22px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mq3 a { color: #101828; text-decoration: none; }
.mq3 a:hover { text-decoration: underline; }
.mq3.paused ul, .mq3:focus-within ul { animation-play-state: paused; }
.mq3 button { width: 30px; height: 30px; border: 0; border-radius: 6px; background: #f2f4f7; color: #344054; font-size: 10px; cursor: pointer; }
.mq3 :focus-visible { outline: 2px solid #528bff; outline-offset: 1px; }
@keyframes mq3 { 0%, 28% { transform: translateY(0); } 33%, 61% { transform: translateY(-22px); } 66%, 94% { transform: translateY(-44px); } 100% { transform: translateY(-66px); } }
@media (prefers-reduced-motion: reduce) { .mq3 ul { animation: none; } }
var w = document.querySelector('.mq3'), b = w.querySelector('button');
b.onclick = function () { var p = w.classList.toggle('paused'); b.setAttribute('aria-pressed', p); b.setAttribute('aria-label', p ? 'Play headlines' : 'Pause headlines'); b.textContent = p ? '▶' : '❚❚'; };

04. Giant Outlined Text Marquee

MarqueeTypography

Huge outlined words scroll across a band, alternating between outlined and solid, a bold divider for agency and event sites. The outline is -webkit-text-stroke with a transparent fill, with a solid fallback where stroke is unsupported.

View code
<div class="mq4" role="img" aria-label="Design, build, launch"><p><span>DESIGN</span> <span class="s">BUILD</span> <span>LAUNCH</span> <span class="s">DESIGN</span> <span>BUILD</span> <span class="s">LAUNCH</span></p></div>
.mq4 { width: 330px; max-width: 100%; overflow: hidden; padding: 8px 0; border-top: 1px solid #334155; border-bottom: 1px solid #334155; }
.mq4 p { display: flex; gap: 24px; width: max-content; margin: 0; font: 900 54px/1 Inter, system-ui, sans-serif; animation: mq4 12s linear infinite; }
.mq4 span { color: transparent; -webkit-text-stroke: 1.5px #e2e8f0; }
.mq4 .s { color: #a3e635; -webkit-text-stroke: 0; }
@supports not (-webkit-text-stroke: 1px #fff) { .mq4 span { color: #e2e8f0; } }
@keyframes mq4 { to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) { .mq4 p { animation: none; } }

05. Announcement Bar with Pause

MarqueeAccessibility

A scrolling promotion bar at the top of a shop with a real pause control. Pausing sets animation-play-state, the button reports its state with aria-pressed, and the bar also pauses while any link inside it has keyboard focus.

View code
<div class="mq5"><div class="vp"><p><span>Free UK shipping over £40</span><span>·</span><span>New colours in the Linen range</span><span>·</span><a href="#sale">Up to 30% off outlet</a><span>·</span></p><p aria-hidden="true"><span>Free UK shipping over £40</span><span>·</span><span>New colours in the Linen range</span><span>·</span><a href="#sale" tabindex="-1">Up to 30% off outlet</a><span>·</span></p></div><button aria-pressed="false">Pause</button></div>
.mq5 { display: flex; align-items: center; width: 340px; max-width: 100%; background: #101828; color: #f2f4f7; font: 600 13px Inter, system-ui, sans-serif; }
.mq5 .vp { display: flex; flex: 1; min-width: 0; overflow: hidden; }
.mq5 p { display: flex; flex: none; gap: 14px; margin: 0; padding: 10px 14px 10px 0; white-space: nowrap; animation: mq5 20s linear infinite; }
.mq5 a { color: #fde047; }
.mq5.paused p, .mq5:focus-within p { animation-play-state: paused; }
.mq5 button { flex: none; padding: 10px 12px; border: 0; border-left: 1px solid #344054; background: transparent; color: #f2f4f7; font: 700 12px Inter, system-ui, sans-serif; cursor: pointer; }
.mq5 :focus-visible { outline: 2px solid #fde047; outline-offset: -2px; }
@keyframes mq5 { to { transform: translateX(-100%); } }
@media (prefers-reduced-motion: reduce) { .mq5 p { animation: none; } }
var w = document.querySelector('.mq5'), b = w.querySelector('button');
b.onclick = function () { var p = w.classList.toggle('paused'); b.setAttribute('aria-pressed', p); b.textContent = p ? 'Play' : 'Pause'; };

06. Tilted Ribbon Marquee

MarqueeRibbon

A coloured ribbon of text runs across the section at a slight angle, an energetic accent for launches and sales. The band is rotated and widened beyond the container so its ends never show, and the overflow is clipped by the section.

View code
<section class="mq6" aria-label="Launch week"><div class="band" aria-hidden="true"><p>LAUNCH WEEK ✦ 20% OFF ✦ LAUNCH WEEK ✦ 20% OFF ✦ LAUNCH WEEK ✦ 20% OFF ✦ </p></div><h3>Launch week: 20% off everything</h3></section>
.mq6 { position: relative; overflow: hidden; display: grid; place-items: end start; width: 320px; max-width: 100%; height: 190px; padding: 18px; box-sizing: border-box; border-radius: 16px; background: #fef3c7; font-family: Inter, system-ui, sans-serif; }
.mq6 .band { position: absolute; left: -20%; right: -20%; top: 40px; padding: 8px 0; background: #111827; transform: rotate(-6deg); overflow: hidden; }
.mq6 p { width: max-content; margin: 0; color: #fde047; font-weight: 900; font-size: 18px; letter-spacing: .06em; animation: mq6 10s linear infinite; }
.mq6 h3 { position: relative; margin: 0; color: #451a03; font-size: 18px; }
@keyframes mq6 { to { transform: translateX(-33.33%); } }
@media (prefers-reduced-motion: reduce) { .mq6 p { animation: none; } }

07. Marquee That Slows on Hover

MarqueeInteraction

Instead of stopping dead, this marquee eases to a crawl when the pointer arrives and speeds up again when it leaves. The duration lives in a custom property, and script swaps it while keeping the current position by converting the elapsed time.

View code
<div class="mq7" aria-label="Featured products"><ul><li>Linen shirt</li><li>Clay mug</li><li>Oak tray</li><li>Wool throw</li></ul><ul aria-hidden="true"><li>Linen shirt</li><li>Clay mug</li><li>Oak tray</li><li>Wool throw</li></ul></div>
.mq7 { display: flex; width: 320px; max-width: 100%; overflow: hidden; font-family: Inter, system-ui, sans-serif; }
.mq7 ul { display: flex; flex: none; gap: 12px; margin: 0; padding: 0 12px 0 0; list-style: none; animation: mq7 8s linear infinite; }
.mq7 li { padding: 18px 20px; border-radius: 14px; background: linear-gradient(135deg, #1e3a8a, #6d28d9); color: #fff; font-weight: 700; white-space: nowrap; }
@keyframes mq7 { to { transform: translateX(-100%); } }
@media (prefers-reduced-motion: reduce) { .mq7 ul { animation: none; } }
var w = document.querySelector('.mq7');
function speed(rate) { w.getAnimations({ subtree: true }).forEach(function (a) { a.updatePlaybackRate(rate); }); }
w.addEventListener('pointerenter', function () { speed(.2); });
w.addEventListener('pointerleave', function () { speed(1); });

About these CSS Infinite Marquees

The seamless loop

Write the content twice side by side, mark the copy aria-hidden, and translate each half by -100% of its own width. When the animation restarts, the copy is exactly where the original began, so there is no jump.

Moving content needs a pause

WCAG requires a way to pause anything that moves for more than five seconds alongside other content. Pause on hover, pause when a link inside has focus, and add a visible Pause button for tickers that carry information.

Duplicates and links

Links in the hidden copy should get tabindex="-1" so keyboard users do not tab through every link twice.

The reduced motion version

Under prefers-reduced-motion stop the animation and let the content wrap or scroll by hand. Nothing in the marquee should be reachable only while it moves.

Frequently asked questions

Is the old marquee element OK to use?

No. It is deprecated and cannot be paused accessibly; these CSS versions replace it.

How do I change the speed?

Change the animation duration, or call updatePlaybackRate() on the running animation as the last demo does.

Will a marquee hurt performance?

Not when it animates transform. Keep the number of items modest and use simple content.