19 / 22
All 22 in 22 CSS Image Galleries
- 01CSS Hexagon Honeycomb Gallery
- 02CSS Aspect-Ratio Justified Gallery
- 03CSS Flexbox Expanding Panel Gallery
- 04CSS :has() Hover Spotlight Gallery
- 05CSS Radial Orbit Image Gallery
- 06CSS Interactive Pointer Trail Gallery
- 07CSS Masonry Image Gallery
- 08CSS Metro Tile Grid Gallery
- 09CSS Magazine Layout Gallery
- 10CSS Lightbox Gallery Overlay
- 11CSS Filmstrip Carousel Gallery
- 12CSS Accordion Image Gallery
- 13CSS Mosaic Zoom Gallery
- 14CSS Glassmorphism Hover Cards
- 15CSS Diagonal Slice Reveal Gallery
- 16CSS Tilt Parallax Image Cards
- 17CSS 3D Cube Image Viewer
- 18CSS Perspective Depth Stack Gallery
- 19CSS Infinite Scroll Image Strip
- 20CSS Ken Burns Slideshow Gallery
- 21CSS Polaroid Stack Gallery
- 22CSS Scattered Photos Gallery
CSS Infinite Scroll Image Strip
A seamless, endlessly looping horizontal banner of images that scrolls forever with no visible seam or reset — the marquee/logo-wall effect brand pages and 'as featured in' rows search for. Pure CSS keyframes.
Build interactive UI like this from scratch: 20 HTML, CSS & JavaScript projects
ZTM · JavaScript Web Projects · 30‑day money-back guarantee · Affiliate link
The code
<section class="gl-18" aria-label="Infinite scrolling image strip">
<div class="gl-18__viewport">
<div class="gl-18__track">
<img src="https://images.unsplash.com/photo-1449157291145-7efd050a4d0e?q=80&w=300&h=300&auto=format&fit=crop" alt="Gallery image 1" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1485846234645-a62644f84728?q=80&w=300&h=300&auto=format&fit=crop" alt="Gallery image 2" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?q=80&w=300&h=300&auto=format&fit=crop" alt="Gallery image 3" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1633356122544-f134324a6cee?q=80&w=300&h=300&auto=format&fit=crop" alt="Gallery image 4" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1531891437562-4301cf35b7e4?q=80&w=300&h=300&auto=format&fit=crop" alt="Gallery image 5" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1504674900247-0877df9cc836?q=80&w=300&h=300&auto=format&fit=crop" alt="Gallery image 6" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1449157291145-7efd050a4d0e?q=80&w=300&h=300&auto=format&fit=crop" alt="" aria-hidden="true" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1485846234645-a62644f84728?q=80&w=300&h=300&auto=format&fit=crop" alt="" aria-hidden="true" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?q=80&w=300&h=300&auto=format&fit=crop" alt="" aria-hidden="true" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1633356122544-f134324a6cee?q=80&w=300&h=300&auto=format&fit=crop" alt="" aria-hidden="true" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1531891437562-4301cf35b7e4?q=80&w=300&h=300&auto=format&fit=crop" alt="" aria-hidden="true" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1504674900247-0877df9cc836?q=80&w=300&h=300&auto=format&fit=crop" alt="" aria-hidden="true" loading="lazy" decoding="async">
</div>
</div>
</section><section class="gl-18" aria-label="Infinite scrolling image strip">
<div class="gl-18__viewport">
<div class="gl-18__track">
<img src="https://images.unsplash.com/photo-1449157291145-7efd050a4d0e?q=80&w=300&h=300&auto=format&fit=crop" alt="Gallery image 1" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1485846234645-a62644f84728?q=80&w=300&h=300&auto=format&fit=crop" alt="Gallery image 2" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?q=80&w=300&h=300&auto=format&fit=crop" alt="Gallery image 3" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1633356122544-f134324a6cee?q=80&w=300&h=300&auto=format&fit=crop" alt="Gallery image 4" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1531891437562-4301cf35b7e4?q=80&w=300&h=300&auto=format&fit=crop" alt="Gallery image 5" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1504674900247-0877df9cc836?q=80&w=300&h=300&auto=format&fit=crop" alt="Gallery image 6" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1449157291145-7efd050a4d0e?q=80&w=300&h=300&auto=format&fit=crop" alt="" aria-hidden="true" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1485846234645-a62644f84728?q=80&w=300&h=300&auto=format&fit=crop" alt="" aria-hidden="true" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?q=80&w=300&h=300&auto=format&fit=crop" alt="" aria-hidden="true" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1633356122544-f134324a6cee?q=80&w=300&h=300&auto=format&fit=crop" alt="" aria-hidden="true" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1531891437562-4301cf35b7e4?q=80&w=300&h=300&auto=format&fit=crop" alt="" aria-hidden="true" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1504674900247-0877df9cc836?q=80&w=300&h=300&auto=format&fit=crop" alt="" aria-hidden="true" loading="lazy" decoding="async">
</div>
</div>
</section>.gl-18,
.gl-18 *,
.gl-18 *::before,
.gl-18 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.gl-18 ::selection {
background: oklch(0.7 0.14 30);
color: #111;
}
.gl-18 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
padding: 40px 0;
background: #0f0f0f;
display: flex;
align-items: center;
justify-content: center;
}
.gl-18__viewport {
width: 100%;
overflow: hidden;
-webkit-mask-image: linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent);
mask-image: linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent);
}
.gl-18__track {
display: flex;
gap: 16px;
width: max-content;
animation: gl-18-marquee 30s linear infinite;
}
.gl-18__viewport:hover .gl-18__track {
animation-play-state: paused;
}
.gl-18__track img {
display: block;
width: 220px;
height: 220px;
object-fit: cover;
border-radius: 12px;
flex: 0 0 auto;
filter: grayscale(1) contrast(1.05);
transition: filter .4s;
}
.gl-18__track img:hover {
filter: none;
}
@keyframes gl-18-marquee {
to {
transform: translateX(-50%);
}
}
@media (prefers-reduced-motion: reduce) {
.gl-18__track {
animation: none;
}
}.gl-18,
.gl-18 *,
.gl-18 *::before,
.gl-18 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.gl-18 ::selection {
background: oklch(0.7 0.14 30);
color: #111;
}
.gl-18 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
padding: 40px 0;
background: #0f0f0f;
display: flex;
align-items: center;
justify-content: center;
}
.gl-18__viewport {
width: 100%;
overflow: hidden;
-webkit-mask-image: linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent);
mask-image: linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent);
}
.gl-18__track {
display: flex;
gap: 16px;
width: max-content;
animation: gl-18-marquee 30s linear infinite;
}
.gl-18__viewport:hover .gl-18__track {
animation-play-state: paused;
}
.gl-18__track img {
display: block;
width: 220px;
height: 220px;
object-fit: cover;
border-radius: 12px;
flex: 0 0 auto;
filter: grayscale(1) contrast(1.05);
transition: filter .4s;
}
.gl-18__track img:hover {
filter: none;
}
@keyframes gl-18-marquee {
to {
transform: translateX(-50%);
}
}
@media (prefers-reduced-motion: reduce) {
.gl-18__track {
animation: none;
}
}/* No JavaScript — a doubled track translated -50% loops seamlessly and forever. */
/* No JavaScript — a doubled track translated -50% loops seamlessly and forever. */Here's a working CSS Image Gallerie 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: CSS Infinite Scroll Image Strip
Source: https://codefronts.com/snippets/css-image-gallery/css-infinite-scroll-image-strip/
A seamless, endlessly looping horizontal banner of images that scrolls forever with no visible seam or reset — the marquee/logo-wall effect brand pages and 'as featured in' rows search for. Pure CSS keyframes.
## HTML
```html
<section class="gl-18" aria-label="Infinite scrolling image strip">
<div class="gl-18__viewport">
<div class="gl-18__track">
<img src="https://images.unsplash.com/photo-1449157291145-7efd050a4d0e?q=80&w=300&h=300&auto=format&fit=crop" alt="Gallery image 1" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1485846234645-a62644f84728?q=80&w=300&h=300&auto=format&fit=crop" alt="Gallery image 2" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?q=80&w=300&h=300&auto=format&fit=crop" alt="Gallery image 3" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1633356122544-f134324a6cee?q=80&w=300&h=300&auto=format&fit=crop" alt="Gallery image 4" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1531891437562-4301cf35b7e4?q=80&w=300&h=300&auto=format&fit=crop" alt="Gallery image 5" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1504674900247-0877df9cc836?q=80&w=300&h=300&auto=format&fit=crop" alt="Gallery image 6" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1449157291145-7efd050a4d0e?q=80&w=300&h=300&auto=format&fit=crop" alt="" aria-hidden="true" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1485846234645-a62644f84728?q=80&w=300&h=300&auto=format&fit=crop" alt="" aria-hidden="true" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?q=80&w=300&h=300&auto=format&fit=crop" alt="" aria-hidden="true" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1633356122544-f134324a6cee?q=80&w=300&h=300&auto=format&fit=crop" alt="" aria-hidden="true" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1531891437562-4301cf35b7e4?q=80&w=300&h=300&auto=format&fit=crop" alt="" aria-hidden="true" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1504674900247-0877df9cc836?q=80&w=300&h=300&auto=format&fit=crop" alt="" aria-hidden="true" loading="lazy" decoding="async">
</div>
</div>
</section>
```
## CSS
```css
.gl-18,
.gl-18 *,
.gl-18 *::before,
.gl-18 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.gl-18 ::selection {
background: oklch(0.7 0.14 30);
color: #111;
}
.gl-18 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
padding: 40px 0;
background: #0f0f0f;
display: flex;
align-items: center;
justify-content: center;
}
.gl-18__viewport {
width: 100%;
overflow: hidden;
-webkit-mask-image: linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent);
mask-image: linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent);
}
.gl-18__track {
display: flex;
gap: 16px;
width: max-content;
animation: gl-18-marquee 30s linear infinite;
}
.gl-18__viewport:hover .gl-18__track {
animation-play-state: paused;
}
.gl-18__track img {
display: block;
width: 220px;
height: 220px;
object-fit: cover;
border-radius: 12px;
flex: 0 0 auto;
filter: grayscale(1) contrast(1.05);
transition: filter .4s;
}
.gl-18__track img:hover {
filter: none;
}
@keyframes gl-18-marquee {
to {
transform: translateX(-50%);
}
}
@media (prefers-reduced-motion: reduce) {
.gl-18__track {
animation: none;
}
}
```
## JavaScript
```js
/* No JavaScript — a doubled track translated -50% loops seamlessly and forever. */
```Here's a working CSS Image Gallerie 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: CSS Infinite Scroll Image Strip
Source: https://codefronts.com/snippets/css-image-gallery/css-infinite-scroll-image-strip/
A seamless, endlessly looping horizontal banner of images that scrolls forever with no visible seam or reset — the marquee/logo-wall effect brand pages and 'as featured in' rows search for. Pure CSS keyframes.
## HTML
```html
<section class="gl-18" aria-label="Infinite scrolling image strip">
<div class="gl-18__viewport">
<div class="gl-18__track">
<img src="https://images.unsplash.com/photo-1449157291145-7efd050a4d0e?q=80&w=300&h=300&auto=format&fit=crop" alt="Gallery image 1" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1485846234645-a62644f84728?q=80&w=300&h=300&auto=format&fit=crop" alt="Gallery image 2" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?q=80&w=300&h=300&auto=format&fit=crop" alt="Gallery image 3" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1633356122544-f134324a6cee?q=80&w=300&h=300&auto=format&fit=crop" alt="Gallery image 4" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1531891437562-4301cf35b7e4?q=80&w=300&h=300&auto=format&fit=crop" alt="Gallery image 5" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1504674900247-0877df9cc836?q=80&w=300&h=300&auto=format&fit=crop" alt="Gallery image 6" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1449157291145-7efd050a4d0e?q=80&w=300&h=300&auto=format&fit=crop" alt="" aria-hidden="true" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1485846234645-a62644f84728?q=80&w=300&h=300&auto=format&fit=crop" alt="" aria-hidden="true" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?q=80&w=300&h=300&auto=format&fit=crop" alt="" aria-hidden="true" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1633356122544-f134324a6cee?q=80&w=300&h=300&auto=format&fit=crop" alt="" aria-hidden="true" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1531891437562-4301cf35b7e4?q=80&w=300&h=300&auto=format&fit=crop" alt="" aria-hidden="true" loading="lazy" decoding="async">
<img src="https://images.unsplash.com/photo-1504674900247-0877df9cc836?q=80&w=300&h=300&auto=format&fit=crop" alt="" aria-hidden="true" loading="lazy" decoding="async">
</div>
</div>
</section>
```
## CSS
```css
.gl-18,
.gl-18 *,
.gl-18 *::before,
.gl-18 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.gl-18 ::selection {
background: oklch(0.7 0.14 30);
color: #111;
}
.gl-18 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
padding: 40px 0;
background: #0f0f0f;
display: flex;
align-items: center;
justify-content: center;
}
.gl-18__viewport {
width: 100%;
overflow: hidden;
-webkit-mask-image: linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent);
mask-image: linear-gradient(90deg,transparent,#000 12%,#000 88%,transparent);
}
.gl-18__track {
display: flex;
gap: 16px;
width: max-content;
animation: gl-18-marquee 30s linear infinite;
}
.gl-18__viewport:hover .gl-18__track {
animation-play-state: paused;
}
.gl-18__track img {
display: block;
width: 220px;
height: 220px;
object-fit: cover;
border-radius: 12px;
flex: 0 0 auto;
filter: grayscale(1) contrast(1.05);
transition: filter .4s;
}
.gl-18__track img:hover {
filter: none;
}
@keyframes gl-18-marquee {
to {
transform: translateX(-50%);
}
}
@media (prefers-reduced-motion: reduce) {
.gl-18__track {
animation: none;
}
}
```
## JavaScript
```js
/* No JavaScript — a doubled track translated -50% loops seamlessly and forever. */
```How this works
The track holds the image set twice back-to-back, then animates transform: translateX() from 0 to -50% on an infinite linear loop. Because the second half is an exact copy of the first, the moment the animation completes a cycle the strip is visually identical to its start — so the loop is seamless with no jump.
The strip pauses on hover so users can inspect an image, edge-fade masks soften the entry/exit, and prefers-reduced-motion stops the marquee entirely (a common accessibility requirement for auto-scrolling content).
Make it yours
- Set speed with the animation
duration; longer = slower drift. - Reverse direction by animating to
+50%or usinganimation-direction: reverse. - Duplicate a second row scrolling the opposite way for a woven look.
- Widen the edge-fade mask for a softer or harder vignette.
Gotchas — read before shipping
- The content must be duplicated exactly and the translate must be
-50%of the doubled track, or the loop visibly jumps. - Always provide a reduced-motion stop — perpetual auto-motion is an accessibility and INP concern.
- Mark the duplicated copy
aria-hidden="true"so screen readers don't announce every image twice.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 15.4+ | 113+ | 111+ |
Floor set by oklch() as this demo is written. The core technique itself goes back further — Chrome 43+.
Keyframe translate + mask edge-fade — universally supported (mask may need -webkit- in older Safari).