Blog / CSS

Create a Modern Infinite Marquee in Pure CSS

Learn to create smooth and infinite horizontal scrolling for your client logos, news tickers and decorative text. Modern technique without JavaScript, with perfect seamless loop.

Introduction to the modern marquee

The HTML <marquee> element has been deprecated for years, but the need to create infinite scrolling is still very present in modern web design. Whether to display client logos, a news banner or decorative text, the marquee remains a popular UI pattern.

In this complete tutorial, we'll explore several modern CSS techniques to create smooth, performant and accessible marquees. No JavaScript is required for basic animations, and we'll see how to add advanced interactions.

💡
Good to know

The secret of a perfect infinite marquee lies in content duplication. By doubling the elements, we create a seamless loop where the end meets the beginning with no visible jump.

CSS infinite animation technique

The foundation of any modern marquee relies on @keyframes and the animation property. The idea is simple: move a container from 0% to -50% of its total width, then restart instantly.

Basic HTML structure

The structure consists of three levels: a container with overflow: hidden, an animated track, and the elements to display. Here is the minimal structure:

marquee-structure.html
<!-- Container with overflow hidden -->
<div class="marquee-container">
  <!-- Piste animee -->
  <div class="marquee">
    <!-- Group 1: original content -->
    <div class="marquee-track">
      <div class="marquee-item">Item 1</div>
      <div class="marquee-item">Item 2</div>
      <div class="marquee-item">Item 3</div>
    </div>
    <!-- Group 2: copy for the loop -->
    <div class="marquee-track" aria-hidden="true">
      <div class="marquee-item">Item 1</div>
      <div class="marquee-item">Item 2</div>
      <div class="marquee-item">Item 3</div>
    </div>
  </div>
</div>

Basic CSS for the animation

The CSS that makes the magic work is surprisingly simple. The animation moves the container from its initial position to -50% (half of its total width, which is exactly the size of the duplicated content):

marquee-base.css
.marquee-container {
  overflow: hidden;
  width: 100%;
}

.marquee {
  display: flex;
  width: max-content;
  animation: marquee-scroll 25s linear infinite;
}

.marquee-track {
  display: flex;
  gap: 24px;
  padding: 0 12px;
}

@keyframes marquee-scroll {
  0% {
    transform: translateX(0);
  }
  100% {
    transform: translateX(-50%);
  }
}

Content duplication for seamless loop

The duplication principle is essential for creating a perfect loop. When the animation reaches -50%, the duplicated content is exactly at the position where the original content was at the start. The reset to 0% is therefore invisible.

🔄
Why -50%?

If you have 6 items and you duplicate them, you have 12 items in total. By moving -50%, you traverse exactly the first 6 items, and at the moment of reset, the next 6 (identical ones) take their place. The transition is invisible because the content is identical.

For accessibility, add aria-hidden="true" on the duplicated group. Screen readers will thus read the content only once.

Logo marquee (clients, partners)

The most frequent use case: displaying a list of client, partner or technology logos. Here is a complete implementation with a modern style:

Stripe
Vercel
Prisma
Notion
Figma
Linear
logo-marquee.css
.logo-item {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px 32px;
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 12px;
  font-weight: 600;
  white-space: nowrap;
  transition: all 0.3s ease;
}

.logo-item:hover {
  background: rgba(99, 102, 241, 0.1);
  border-color: rgba(99, 102, 241, 0.3);
}

/* Fade effect on edges */
.marquee-fade {
  position: relative;
}

.marquee-fade::before,
.marquee-fade::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  width: 100px;
  z-index: 2;
  pointer-events: none;
}

.marquee-fade::before {
  left: 0;
  background: linear-gradient(90deg, var(--bg-card), transparent);
}

.marquee-fade::after {
  right: 0;
  background: linear-gradient(-90deg, var(--bg-card), transparent);
}

Text marquee (news ticker)

The text marquee is ideal for news banners, announcements or decorative elements. You can play with typography to create impressive visual effects.

Large format decorative text

An XXL text with gradient effect scrolling in the background, perfect for hero sections or page dividers:

Effect.Labs * CSS Magic * Web Design * Creative Dev *
text-marquee.css
.text-marquee {
  display: flex;
  gap: 48px;
}

.text-marquee-item {
  font-size: 4rem;
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  white-space: nowrap;

  /* Gradient text */
  background: linear-gradient(135deg, #6366f1, #8b5cf6, #d946ef);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

.text-marquee-separator {
  font-size: 4rem;
  color: #6366f1;
  opacity: 0.5;
}

News ticker with badges

A professional news banner with colored badges to categorize information:

New CSS Container Queries now supported by all browsers
Hot View Transitions API revolutionizes SPAs
Update Tailwind CSS 4.0 announced for Q1 2026
Info New CSS Anchor Positioning spec in progress
ticker-marquee.css
.ticker-marquee {
  background: rgba(99, 102, 241, 0.1);
  border-top: 1px solid rgba(99, 102, 241, 0.2);
  border-bottom: 1px solid rgba(99, 102, 241, 0.2);
  padding: 12px 0;
}

.ticker-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 24px;
  white-space: nowrap;
}

.ticker-badge {
  padding: 4px 10px;
  background: #6366f1;
  border-radius: 4px;
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
}

.ticker-badge.new { background: #10b981; }
.ticker-badge.hot { background: #ef4444; }
.ticker-badge.update { background: #f59e0b; }

Pause on hover

An essential feature for UX: pausing the scroll when the user hovers over the marquee. This allows reading the content comfortably.

💡
UX tip

Add a slight visual change on hover (opacity, background color) to indicate to the user that the marquee is interactive and can be paused.

The implementation is very simple thanks to animation-play-state:

pause-hover.css
/* Pause animation on container hover */
.marquee-container:hover .marquee {
  animation-play-state: paused;
}

/* Or directly on the animated element */
.marquee:hover {
  animation-play-state: paused;
}

/* Optional visual feedback */
.marquee-container:hover {
  background: rgba(99, 102, 241, 0.05);
}

Reverse direction and variable speeds

To create interesting visual compositions, combine multiple marquees with different directions and speeds. The horizontal parallax effect is very effective for giving depth.

Reversed direction

Use animation-direction: reverse to reverse the scroll direction:

React
Vue.js
Angular
Svelte
Next.js
Nuxt
TypeScript
Tailwind
Prisma
GraphQL
tRPC
Zod
direction-speed.css
/* Reversed direction */
.marquee-reverse {
  animation-direction: reverse;
}

/* Vitesses differentes */
.marquee-slow {
  animation-duration: 40s;
}

.marquee-fast {
  animation-duration: 15s;
}

/* Default speed */
.marquee {
  animation-duration: 25s;
}

/* Combine for parallax effect */
.marquee-layer-1 { animation-duration: 30s; }
.marquee-layer-2 { animation-duration: 20s; animation-direction: reverse; }
.marquee-layer-3 { animation-duration: 25s; }

Content-adaptive speed

For a constant speed regardless of the content quantity, calculate the duration based on the width:

adaptive-speed.js
// Speed in pixels per second
const SPEED = 50; // px/s

function setMarqueeSpeed(marquee) {
  const track = marquee.querySelector('.marquee-track');
  const width = track.offsetWidth;
  const duration = width / SPEED;

  marquee.style.animationDuration = `${duration}s`;
}

// Apply to all marquees
document.querySelectorAll('.marquee').forEach(setMarqueeSpeed);

Responsive and accessibility

A good marquee must be adaptive and accessible. Here are the best practices to follow for an optimal experience on all devices and for all users.

Responsive design

Adapt the element size and speed according to screen size:

responsive-marquee.css
/* Mobile: smaller elements, reduced speed */
@media (max-width: 768px) {
  .marquee {
    animation-duration: 35s;
  }

  .logo-item {
    padding: 12px 20px;
    font-size: 0.9rem;
  }

  .text-marquee-item {
    font-size: 2.5rem;
  }

  .marquee-track {
    gap: 16px;
  }
}

/* Very wide screens: increased speed */
@media (min-width: 1920px) {
  .marquee {
    animation-duration: 20s;
  }
}

Accessibility

Accessibility is crucial. Respect user preferences for animations:

accessibility.css
/* Respect prefers-reduced-motion */
@media (prefers-reduced-motion: reduce) {
  .marquee {
    animation: none;
  }

  /* Display content statically */
  .marquee-track[aria-hidden="true"] {
    display: none;
  }

  .marquee-container {
    overflow-x: auto;
  }
}

/* Hide duplicated content from screen readers */
.marquee-track[aria-hidden="true"] {
  /* Already handled by aria-hidden="true" */
}

/* Visible focus for keyboard navigation */
.logo-item:focus-visible {
  outline: 2px solid #6366f1;
  outline-offset: 2px;
}
⚠️
Watch out for automatic animations

Continuously moving animations can cause problems for people with vestibular disorders. Always respect prefers-reduced-motion and provide a way to stop the animation.

Performance

A few tips for performant marquees:

  • Use transform instead of left or margin for the animation - this triggers GPU compositing
  • Add will-change: transform to prepare the browser for the animation
  • Avoid too many elements in the same marquee - merge SVGs if possible
  • Limit the number of marquees animated simultaneously on the page
performance.css
.marquee {
  /* Optimisation performance */
  will-change: transform;
  backface-visibility: hidden;
  perspective: 1000px;
}

/* Disable will-change after animation */
.marquee.loaded {
  will-change: auto;
}

Conclusion

The modern CSS marquee is a powerful tool for adding movement and visual interest to your pages. By mastering the duplication technique and animation properties, you can create smooth and professional scrolling effects.

Key points to remember:

  • Duplicate the content for a perfect seamless loop
  • Use translateX(-50%) for the animation
  • Add pause on hover for better UX
  • Respect prefers-reduced-motion for accessibility
  • Combine directions and speeds for creative effects
🎨
Go further

Find ready-to-use and customizable marquees in our effects library, with one-click copyable code.