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.
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 & Co</li><li>Brew Lab</li><li>Linea</li></ul><ul class="t" aria-hidden="true"><li>Northwind</li><li>Lumen</li><li>Kiln & Co</li><li>Brew Lab</li><li>Linea</li></ul></div>
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>
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>
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.
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>
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>
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.
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.