22 CSS Image Carousels03 / 22
Infinite Logo Scroller
A seamless, endlessly looping marquee of partner or client logos — the 'trusted by' strip on almost every SaaS landing page. Pure CSS keyframes translate a duplicated track, edges fade under a mask, and the animation pauses on hover.
Published
The code
<section class="car-03" aria-label="Trusted by leading teams">
<p class="car-03__eyebrow">Trusted by 4,000+ teams</p>
<div class="car-03__viewport">
<ul class="car-03__track">
<li class="car-03__item">NORTHWIND</li>
<li class="car-03__item">Lumen&Co</li>
<li class="car-03__item">ARCADIA</li>
<li class="car-03__item">Föhn</li>
<li class="car-03__item">Kestrel</li>
<li class="car-03__item">MONOLITH</li>
<li class="car-03__item" aria-hidden="true">NORTHWIND</li>
<li class="car-03__item" aria-hidden="true">Lumen&Co</li>
<li class="car-03__item" aria-hidden="true">ARCADIA</li>
<li class="car-03__item" aria-hidden="true">Föhn</li>
<li class="car-03__item" aria-hidden="true">Kestrel</li>
<li class="car-03__item" aria-hidden="true">MONOLITH</li>
</ul>
</div>
</section><section class="car-03" aria-label="Trusted by leading teams">
<p class="car-03__eyebrow">Trusted by 4,000+ teams</p>
<div class="car-03__viewport">
<ul class="car-03__track">
<li class="car-03__item">NORTHWIND</li>
<li class="car-03__item">Lumen&Co</li>
<li class="car-03__item">ARCADIA</li>
<li class="car-03__item">Föhn</li>
<li class="car-03__item">Kestrel</li>
<li class="car-03__item">MONOLITH</li>
<li class="car-03__item" aria-hidden="true">NORTHWIND</li>
<li class="car-03__item" aria-hidden="true">Lumen&Co</li>
<li class="car-03__item" aria-hidden="true">ARCADIA</li>
<li class="car-03__item" aria-hidden="true">Föhn</li>
<li class="car-03__item" aria-hidden="true">Kestrel</li>
<li class="car-03__item" aria-hidden="true">MONOLITH</li>
</ul>
</div>
</section>.car-03,
.car-03 *,
.car-03 *::before,
.car-03 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.car-03 ::selection {
background: #111827;
color: #fff;
}
.car-03 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
padding: 40px 0;
background: #f6f7f9;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 22px;
}
.car-03__eyebrow {
font-size: 12px;
letter-spacing: .16em;
text-transform: uppercase;
color: #9aa2b1;
}
.car-03__viewport {
width: min(720px,100%);
overflow: hidden;
-webkit-mask: linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent);
mask: linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent);
}
.car-03__track {
list-style: none;
display: flex;
align-items: center;
gap: 64px;
width: max-content;
animation: car-03-scroll 24s linear infinite;
}
.car-03__viewport:hover .car-03__track,
.car-03__viewport:focus-within .car-03__track {
animation-play-state: paused;
}
.car-03__item {
flex: none;
font-size: 22px;
font-weight: 800;
letter-spacing: .02em;
color: #4b5361;
white-space: nowrap;
font-family: Georgia,'Times New Roman',serif;
opacity: .75;
}
@keyframes car-03-scroll {
from {
transform: translateX(0);
}
to {
transform: translateX(-50%);
}
}
@media (prefers-reduced-motion: reduce) {
.car-03__track {
animation: none;
flex-wrap: wrap;
justify-content: center;
gap: 32px 48px;
width: auto;
}
}.car-03,
.car-03 *,
.car-03 *::before,
.car-03 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.car-03 ::selection {
background: #111827;
color: #fff;
}
.car-03 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
padding: 40px 0;
background: #f6f7f9;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 22px;
}
.car-03__eyebrow {
font-size: 12px;
letter-spacing: .16em;
text-transform: uppercase;
color: #9aa2b1;
}
.car-03__viewport {
width: min(720px,100%);
overflow: hidden;
-webkit-mask: linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent);
mask: linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent);
}
.car-03__track {
list-style: none;
display: flex;
align-items: center;
gap: 64px;
width: max-content;
animation: car-03-scroll 24s linear infinite;
}
.car-03__viewport:hover .car-03__track,
.car-03__viewport:focus-within .car-03__track {
animation-play-state: paused;
}
.car-03__item {
flex: none;
font-size: 22px;
font-weight: 800;
letter-spacing: .02em;
color: #4b5361;
white-space: nowrap;
font-family: Georgia,'Times New Roman',serif;
opacity: .75;
}
@keyframes car-03-scroll {
from {
transform: translateX(0);
}
to {
transform: translateX(-50%);
}
}
@media (prefers-reduced-motion: reduce) {
.car-03__track {
animation: none;
flex-wrap: wrap;
justify-content: center;
gap: 32px 48px;
width: auto;
}
}/* No JavaScript — a duplicated track translated -50% loops seamlessly; hover/focus pauses it. */
/* No JavaScript — a duplicated track translated -50% loops seamlessly; hover/focus pauses it. */Here's a working CSS Image Carousel from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Infinite Logo Scroller
Source: https://codefronts.com/snippets/css-image-carousel/infinite-logo-scroller/
A seamless, endlessly looping marquee of partner or client logos — the 'trusted by' strip on almost every SaaS landing page. Pure CSS keyframes translate a duplicated track, edges fade under a mask, and the animation pauses on hover.
## HTML
```html
<section class="car-03" aria-label="Trusted by leading teams">
<p class="car-03__eyebrow">Trusted by 4,000+ teams</p>
<div class="car-03__viewport">
<ul class="car-03__track">
<li class="car-03__item">NORTHWIND</li>
<li class="car-03__item">Lumen&Co</li>
<li class="car-03__item">ARCADIA</li>
<li class="car-03__item">Föhn</li>
<li class="car-03__item">Kestrel</li>
<li class="car-03__item">MONOLITH</li>
<li class="car-03__item" aria-hidden="true">NORTHWIND</li>
<li class="car-03__item" aria-hidden="true">Lumen&Co</li>
<li class="car-03__item" aria-hidden="true">ARCADIA</li>
<li class="car-03__item" aria-hidden="true">Föhn</li>
<li class="car-03__item" aria-hidden="true">Kestrel</li>
<li class="car-03__item" aria-hidden="true">MONOLITH</li>
</ul>
</div>
</section>
```
## CSS
```css
.car-03,
.car-03 *,
.car-03 *::before,
.car-03 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.car-03 ::selection {
background: #111827;
color: #fff;
}
.car-03 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
padding: 40px 0;
background: #f6f7f9;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 22px;
}
.car-03__eyebrow {
font-size: 12px;
letter-spacing: .16em;
text-transform: uppercase;
color: #9aa2b1;
}
.car-03__viewport {
width: min(720px,100%);
overflow: hidden;
-webkit-mask: linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent);
mask: linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent);
}
.car-03__track {
list-style: none;
display: flex;
align-items: center;
gap: 64px;
width: max-content;
animation: car-03-scroll 24s linear infinite;
}
.car-03__viewport:hover .car-03__track,
.car-03__viewport:focus-within .car-03__track {
animation-play-state: paused;
}
.car-03__item {
flex: none;
font-size: 22px;
font-weight: 800;
letter-spacing: .02em;
color: #4b5361;
white-space: nowrap;
font-family: Georgia,'Times New Roman',serif;
opacity: .75;
}
@keyframes car-03-scroll {
from {
transform: translateX(0);
}
to {
transform: translateX(-50%);
}
}
@media (prefers-reduced-motion: reduce) {
.car-03__track {
animation: none;
flex-wrap: wrap;
justify-content: center;
gap: 32px 48px;
width: auto;
}
}
```
## JavaScript
```js
/* No JavaScript — a duplicated track translated -50% loops seamlessly; hover/focus pauses it. */
```Here's a working CSS Image Carousel from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Infinite Logo Scroller
Source: https://codefronts.com/snippets/css-image-carousel/infinite-logo-scroller/
A seamless, endlessly looping marquee of partner or client logos — the 'trusted by' strip on almost every SaaS landing page. Pure CSS keyframes translate a duplicated track, edges fade under a mask, and the animation pauses on hover.
## HTML
```html
<section class="car-03" aria-label="Trusted by leading teams">
<p class="car-03__eyebrow">Trusted by 4,000+ teams</p>
<div class="car-03__viewport">
<ul class="car-03__track">
<li class="car-03__item">NORTHWIND</li>
<li class="car-03__item">Lumen&Co</li>
<li class="car-03__item">ARCADIA</li>
<li class="car-03__item">Föhn</li>
<li class="car-03__item">Kestrel</li>
<li class="car-03__item">MONOLITH</li>
<li class="car-03__item" aria-hidden="true">NORTHWIND</li>
<li class="car-03__item" aria-hidden="true">Lumen&Co</li>
<li class="car-03__item" aria-hidden="true">ARCADIA</li>
<li class="car-03__item" aria-hidden="true">Föhn</li>
<li class="car-03__item" aria-hidden="true">Kestrel</li>
<li class="car-03__item" aria-hidden="true">MONOLITH</li>
</ul>
</div>
</section>
```
## CSS
```css
.car-03,
.car-03 *,
.car-03 *::before,
.car-03 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.car-03 ::selection {
background: #111827;
color: #fff;
}
.car-03 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
padding: 40px 0;
background: #f6f7f9;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 22px;
}
.car-03__eyebrow {
font-size: 12px;
letter-spacing: .16em;
text-transform: uppercase;
color: #9aa2b1;
}
.car-03__viewport {
width: min(720px,100%);
overflow: hidden;
-webkit-mask: linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent);
mask: linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent);
}
.car-03__track {
list-style: none;
display: flex;
align-items: center;
gap: 64px;
width: max-content;
animation: car-03-scroll 24s linear infinite;
}
.car-03__viewport:hover .car-03__track,
.car-03__viewport:focus-within .car-03__track {
animation-play-state: paused;
}
.car-03__item {
flex: none;
font-size: 22px;
font-weight: 800;
letter-spacing: .02em;
color: #4b5361;
white-space: nowrap;
font-family: Georgia,'Times New Roman',serif;
opacity: .75;
}
@keyframes car-03-scroll {
from {
transform: translateX(0);
}
to {
transform: translateX(-50%);
}
}
@media (prefers-reduced-motion: reduce) {
.car-03__track {
animation: none;
flex-wrap: wrap;
justify-content: center;
gap: 32px 48px;
width: auto;
}
}
```
## JavaScript
```js
/* No JavaScript — a duplicated track translated -50% loops seamlessly; hover/focus pauses it. */
```How this works
The track holds the logo list twice back-to-back. A single @keyframes translates the track by exactly -50% on the X axis; because the second half is an identical copy, the reset at 100% is visually invisible — an infinite loop with no JavaScript timer. Using translateX (a transform) keeps the whole marquee on the compositor thread.
The duplicated half is marked aria-hidden so assistive tech reads each logo only once. A horizontal mask-image feathers both edges so logos dissolve in and out instead of clipping. Hovering (or focusing) the strip sets animation-play-state:paused so users can read a name.
Make it yours
- Set the loop speed with the track's
animation-duration(longer = slower). - Reverse direction by switching the keyframe to translate from
-50%to0, or addanimation-direction:reverse. - Widen or tighten the edge fade via the
mask-imagegradient stops. - Change logo spacing with the track's
gap.
Gotchas — read before shipping
- The track MUST contain exactly two identical copies, and the keyframe MUST end at
-50%, or the loop will visibly jump. - Mark the duplicated set
aria-hidden="true"so screen readers don't announce every logo twice. - Pause on hover/focus so users chasing a specific logo aren't fighting the motion.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 53+ | 15.4+ | 53+ | 53+ |
CSS animation is universal; mask-image needs -webkit-mask in older Safari (included).