The old <marquee> element can still appear in legacy code, but it is obsolete and should not be the foundation of a new component. A modern marquee uses normal semantic HTML plus CSS animation.
Start with a clipped viewport
The outer element defines the visible area. The inner track moves across it.
<div class="marquee" aria-label="Shipping is free this weekend">
<span class="marquee__text">Shipping is free this weekend</span>
</div>
.marquee { overflow: hidden; }
.marquee__text {
display: inline-block;
min-width: 100%;
white-space: nowrap;
animation: marquee-left 14s linear infinite;
}
@keyframes marquee-left {
from { transform: translateX(100%); }
to { transform: translateX(-100%); }
}
.marquee:hover .marquee__text,
.marquee:focus-within .marquee__text { animation-play-state: paused; }
@media (prefers-reduced-motion: reduce) {
.marquee__text { animation: none; white-space: normal; }
}
Seamless continuous marquees
A single message leaves a gap while it exits and re-enters. For a continuous logo strip or ticker, duplicate the presentation track and animate exactly one copy-width. The duplicated copy should be hidden from assistive technology so the message is not announced twice.
Avoid speed settings that mean different things
Some plugins label a number as “speed” even though it is really duration. A clearer UI says “Duration: 12 seconds” and explains that a larger value moves more slowly. If you calculate pixels per second instead, display that unit explicitly.
Pause and reduced motion
Pause-on-hover is helpful but keyboard users also need a way to stop movement. For informational tickers, include a visible pause button. Always provide a prefers-reduced-motion fallback that removes continuous motion.
The Marquee Text Generator creates this pattern visually and outputs the HTML and CSS separately or together.