Build a CSS Scrolling Text Marquee in Webflow (No Plugin)
Build an infinite scrolling text marquee in Webflow with pure CSS, no plugin and no dead marquee tag. The duplicate-track and translateX trick, explained.
Here's the short version, because you came here to ship something: to build a scrolling text marquee in pure CSS, you put your content in a track, duplicate that content once so the loop has something to slide into, and animate the whole track with @keyframes and transform: translateX(-50%). No plugin. No JavaScript. And definitely no <marquee> tag — that thing has been dead and deprecated since before some of your interns were born.
Here's the entire technique in one block:
.marquee {
overflow: hidden; /* hide everything outside the window */
white-space: nowrap;
}
.marquee__track {
display: inline-flex;
width: max-content; /* let the content decide the width */
animation: marquee 20s linear infinite;
}
@keyframes marquee {
from { transform: translateX(0); }
to { transform: translateX(-50%); } /* slide exactly one copy's width */
}
<div class="marquee">
<div class="marquee__track">
<span>Veteran-owned Webflow shop. ·</span>
<span>Built in Wyoming. ·</span>
<!-- duplicate the exact same set again -->
<span aria-hidden="true">Veteran-owned Webflow shop. ·</span>
<span aria-hidden="true">Built in Wyoming. ·</span>
</div>
</div>
That's it. That's the whole trick. The rest of this post is the why — why the duplicate matters, how to wire it into Webflow without losing your mind, and how to keep it from making someone motion-sick. Stick around, because the seamless-loop part is where most tutorials quietly lie to you.
Table of contents
- Why the duplicate-track trick is the whole game
- The real CSS, line by line
- Building it in Webflow (no plugin)
- Pausing on hover and controlling speed
- Accessibility: respect prefers-reduced-motion
- Frequently asked questions
Why the duplicate-track trick is the whole game {#why-duplicate}
The naive way to build a marquee is to animate one strip of text from translateX(100%) to translateX(-100%) — push it on from the right, run it off to the left. It works for about three seconds, and then you watch a giant empty gap drag across the screen while the text re-enters. Pretty. Pointless.
The fix is almost embarrassingly simple: render your content twice in a row, then only slide the track by half its width. When the first copy has scrolled fully off to the left, the second copy is sitting in the exact spot the first one started. The animation loops back to zero, and because copy two looks identical to copy one, the human eye never catches the seam. It's the same illusion a cartoon background uses when Fred Flintstone runs past the same lamp forty times.
The math is the part to internalize: you duplicate the content once (two total copies), so you animate to translateX(-50%). If you paste the content three times, you'd animate to -33.33%. Get that fraction wrong and you get a visible jump on every loop — the marquee equivalent of a song skipping. One wrong value breaks the whole illusion. Welcome to front-end; it's mapping all the way down.
The real CSS, line by line {#the-css}
Let's slow down and look at what each piece is doing, because copy-paste without understanding is how you end up filing a support ticket against your own website.
.marquee {
overflow: hidden;
white-space: nowrap; /* keep it all on one line */
-webkit-mask-image: linear-gradient(
to right, transparent, #000 8%, #000 92%, transparent
); /* soft fade at both edges — optional but classy */
mask-image: linear-gradient(
to right, transparent, #000 8%, #000 92%, transparent
);
}
.marquee__track {
display: inline-flex;
gap: 2rem; /* breathing room between items */
width: max-content;
will-change: transform; /* hint the browser to use the GPU */
animation: marquee 20s linear infinite;
}
@keyframes marquee {
from { transform: translateX(0); }
to { transform: translateX(-50%); }
}
A few things worth saying out loud. We animate transform, not margin or left, on purpose — transforms are GPU-composited, so the browser repaints almost nothing and you keep a smooth 60fps even on a tired old phone. Animating left instead would thrash the layout on every frame and your "elegant" marquee would judder like a shopping cart with a bad wheel.
linear as the timing function is non-negotiable. A marquee with ease-in-out speeds up and slows down on every loop, which looks like the text is breathing. You want a constant, boring, metronome pace. The mask-image fade is the one truly optional line — it just softens the hard cut at each edge so text doesn't pop in and out like it hit a wall. Small touch, big polish.
Building it in Webflow (no plugin) {#in-webflow}
You do not need a marquee plugin, and you don't need to drop $30 on a "scrolling ticker" Webflow clonable that's really just this same CSS with someone else's class names on it. Here's the clean way to wire it inside the Designer.
- Drop a Div Block. Call the class
marquee. In its style settings set Overflow: Hidden. - Inside it, add another Div Block named
marquee-track. Set its Display to Flex, direction horizontal, and add a gap. - Build your content once inside the track — text blocks, logos, whatever you're scrolling.
- Select the whole content group and copy-paste it once so the track holds two identical sets. (This is the duplicate from earlier. Yes, it feels wrong to paste it twice. Do it anyway.)
- The keyframe animation is the one bit Webflow's Interactions panel handles awkwardly, so add it as custom CSS. Open Page Settings → Custom Code → Inside
<head>(or a global embed in your site settings) and paste:
<style>
.marquee { overflow: hidden; white-space: nowrap; }
.marquee-track {
width: max-content;
animation: marquee 20s linear infinite;
}
@keyframes marquee {
from { transform: translateX(0); }
to { transform: translateX(-50%); }
}
</style>
That's the pattern we reach for on real client builds, and it's the kind of thing that keeps a Webflow site from rotting into a junk drawer by month nine. If you'd rather not hand-roll it, our Webflow development agency does this sort of plumbing for a living. And if you're collecting tools for builds like this, the 13 best Webflow resource sites roundup is a solid bookmark.
Pausing on hover and controlling speed {#pause-speed}
Two requests come up every single time someone ships a marquee: let me pause it when I hover and it's going too fast / too slow.
Pause-on-hover is one line:
.marquee:hover .marquee__track {
animation-play-state: paused;
}
Speed is just the duration. Bigger number, slower scroll. There's no magic "speed" property — a marquee that takes 40s to complete is half the pace of one set to 20s. If you want it to read like a calm news ticker, push it to 30s or 40s. If you want frantic Times-Square energy, drop it to 10s. Pick the pace that lets a human actually read the words, because a marquee nobody can read is just decoration, and decoration that wins design awards still loses customers.
Accessibility: respect prefers-reduced-motion {#accessibility}
Here's the part most tutorials skip, and it's the part that actually matters. Constant looping motion is a genuine problem for people with vestibular disorders — it can trigger dizziness and nausea the same way a shaky car ride does. Browsers expose a setting for exactly this, and it costs you nothing to honor it:
@media (prefers-reduced-motion: reduce) {
.marquee__track {
animation: none;
/* optionally let it scroll manually instead */
overflow-x: auto;
white-space: nowrap;
}
}
When a visitor has "reduce motion" turned on in their OS, the scroll stops dead and they can read at their own pace. You ship one media query and you stop making a slice of your audience feel sick. That's not a nice-to-have; it's table stakes.
Two more quick a11y notes. Mark the duplicated copy aria-hidden="true" so a screen reader doesn't read your tagline twice in a row like a malfunctioning robot. And if the marquee holds links, make sure people can still tab to and focus them — a moving target you can't reliably click is a usability bug wearing a fancy hat.
Get those three things right — reduced-motion, aria-hidden on the dupe, focusable links — and your marquee is accessible, performant, and plugin-free. Which is more than you can say for most of the ones in the wild.
Frequently asked questions {#faq}
How do you make a scrolling marquee in CSS?
Wrap your content in a container with overflow: hidden, place it in an inner track, duplicate the content once inside that track, and animate the track with @keyframes from translateX(0) to translateX(-50%) using linear infinite timing. The duplicate is what makes the loop seamless.
Is the <marquee> HTML tag still usable?
Technically a few browsers still render it, but it's been deprecated for years and can be dropped at any time. It also gives you no control over accessibility or performance. Use the CSS animation approach instead — it's not much more code and it actually behaves.
Why does my CSS marquee have a gap or jump when it loops?
You almost certainly have the wrong translateX end value for your number of copies. Two copies should animate to -50%, three copies to -33.33%. A gap means the track is sliding farther than one copy's width; a jump means it isn't sliding far enough.
How do I make the marquee pause when someone hovers over it?
Add .marquee:hover .marquee-track { animation-play-state: paused; }. The animation freezes on hover and resumes when the cursor leaves — handy when the marquee contains links people need to click.
Does an infinite scrolling marquee hurt SEO? Not on its own. The text lives in real HTML, so search engines read it fine. Just don't bury your only copy of important content (like your H1 or primary keyword) inside a moving element, and keep the animation off the critical rendering path.
Before you ship it
A marquee is a small thing that's surprisingly easy to get wrong — the gap, the juddery left animation, the missing reduced-motion query that quietly alienates real users. Get the duplicate-track pattern right once and you'll never reach for a plugin again. If you'd rather hand the whole Webflow build to a senior bench that's wired this stuff into 100-plus sites, that's what we do. Either way, paste responsibly.