Below is a pure HTML/CSS/JS implementation you can paste straight into Shopify. It auto-duplicates your logos so the strip loops seamlessly in either direction at any speed you choose.
This is an example implementation — replace the image URLs, branding, colors, speed, and direction to match your store.
Where to paste this in Shopify
- Go to Online Store → Themes → Customize
- Open the template (Home is typical)
- Add section → Custom HTML (or Custom Liquid)
- Paste the entire snippet below
- Save and preview
Copy-Paste Example — Scrolling Logo Carousel
Paste everything from <!DOCTYPE html> to </html>.
<body>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shopify Scrolling Logo Carousel</title>
<style>
:root{
/* Tweak these tokens to match your brand */
--lc-bg: #0f1115; /* section background */
--lc-fg: #e8ecf1; /* optional heading color */
--lc-height: 84px; /* logo rail height */
--lc-gap: 32px; /* space between logos */
--lc-pad: 20px; /* outer padding */
--lc-grayscale: 0; /* set to 1 for grayscale logos */
--lc-opacity: 1; /* lower to .8 if you want subtle look */
}
html,body{ margin:0; font-family: Arial, sans-serif; background:#0b0d12; color:var(--lc-fg); }
.logo-carousel{
background: var(--lc-bg);
padding: var(--lc-pad) 0;
overflow: hidden;
position: relative;
}
/* Optional heading */
.lc-head{
max-width: 1200px;
margin: 0 auto 8px;
padding: 0 18px;
display: flex; align-items: center; justify-content: space-between; gap: 12px;
}
.lc-title{ font-size: 18px; letter-spacing:.2px; opacity:.9; }
/* Viewport for the moving track */
.lc-viewport{
position: relative;
max-width: 1200px;
margin: 0 auto;
padding: 0 18px;
overflow: hidden;
}
/* The moving track */
.lc-track{
display: flex;
align-items: center;
gap: var(--lc-gap);
height: var(--lc-height);
will-change: transform;
filter: grayscale(var(--lc-grayscale));
opacity: var(--lc-opacity);
}
.lc-item{
flex: 0 0 auto;
display: inline-flex; align-items: center; justify-content: center;
height: 100%;
}
.lc-item img{
height: 100%;
width: auto;
object-fit: contain;
display: block;
pointer-events: none;
user-select: none;
}
/* Animation states */
.lc-animate-left { animation: lc-marquee-left linear infinite; animation-duration: var(--lc-duration, 20s); }
.lc-animate-right { animation: lc-marquee-right linear infinite; animation-duration: var(--lc-duration, 20s); }
@keyframes lc-marquee-left { 0% { transform: translateX(0); } 100% { transform: translateX(var(--lc-shift, -50%)); } }
@keyframes lc-marquee-right { 0% { transform: translateX(var(--lc-shift, -50%)); } 100% { transform: translateX(0); } }
/* Pause on hover */
.logo-carousel.paused .lc-track{ animation-play-state: paused !important; }
/* Subtle edges fade (optional). Remove if you want hard edges. */
.lc-fade-left,
.lc-fade-right{
position: absolute; top: 0; bottom:0; width: 60px; pointer-events: none;
background: linear-gradient(to right, var(--lc-bg), transparent);
z-index: 2;
}
.lc-fade-right{
right: 0; left: auto;
background: linear-gradient(to left, var(--lc-bg), transparent);
}
/* Responsive tweaks */
@media (max-width: 740px){
:root{ --lc-height: 64px; --lc-gap: 24px; }
.lc-title{ font-size: 16px; }
}
@media (max-width: 460px){
:root{ --lc-height: 56px; --lc-gap: 20px; }
}
</style>
<section class="logo-carousel" id="logoCarousel" data-speed="80" pixels per second>
data-direction="left" <!-- 'left' or 'right' -->
data-duplicate="2" <!-- how many times to duplicate logos (min 2) -->
data-pause-on-hover="true">
<div class="lc-head">
<div class="lc-title">Trusted by teams and brands worldwide</div>
</div>
<div class="lc-viewport">
<div class="lc-fade-left" aria-hidden="true"></div>
<div class="lc-fade-right" aria-hidden="true"></div>
<!-- Track: put ONE set of logos here; JS will duplicate for seamless loop -->
<div class="lc-track" id="lcTrack">
<!-- Replace these logo URLs with your own (transparent PNG/SVG recommended) -->
<div class="lc-item"><img src="https://upload.wikimedia.org/wikipedia/commons/a/ab/Apple-logo.png" alt="Apple"></div>
<div class="lc-item"><img src="https://upload.wikimedia.org/wikipedia/commons/4/44/Microsoft_logo.svg" alt="Microsoft"></div>
<div class="lc-item"><img src="https://upload.wikimedia.org/wikipedia/commons/5/51/IBM_logo.svg" alt="IBM"></div>
<div class="lc-item"><img src="https://upload.wikimedia.org/wikipedia/commons/a/a9/Amazon_logo.svg" alt="Amazon"></div>
<div class="lc-item"><img src="https://upload.wikimedia.org/wikipedia/commons/2/2f/Google_2015_logo.svg" alt="Google"></div>
<div class="lc-item"><img src="https://upload.wikimedia.org/wikipedia/commons/0/08/Spotify_logo_with_text.svg" alt="Spotify"></div>
<div class="lc-item"><img src="https://upload.wikimedia.org/wikipedia/commons/0/02/YouTube_social_white_squircle_%282017%29.svg" alt="YouTube"></div>
<div class="lc-item"><img src="https://upload.wikimedia.org/wikipedia/commons/1/19/PayPal.svg" alt="PayPal"></div>
</div>
</div>
</section>
<script>
(function(){
const root = document.getElementById('logoCarousel');
const track = document.getElementById('lcTrack');
if(!root || !track) return;
const dir = (root.getAttribute('data-direction') || 'left').toLowerCase();
const speed = Math.max(20, parseInt(root.getAttribute('data-speed') || '80', 10)); // px/sec
const dup = Math.max(2, parseInt(root.getAttribute('data-duplicate') || '2', 10));
const pauseOnHover = root.getAttribute('data-pause-on-hover') === 'true';
// 1) Ensure we have enough logos duplicated to loop seamlessly
function duplicateChildren(times){
const children = Array.from(track.children);
for(let t=1; t<times; t++){
children.forEach(ch => track.appendChild(ch.cloneNode(true)));
}
}
duplicateChildren(dup);
// 2) Compute total width and set shift/duration
function setup(){
root.classList.remove('paused');
track.classList.remove('lc-animate-left','lc-animate-right');
// total width of all children
const total = Array.from(track.children).reduce((w, el) => w + el.getBoundingClientRect().width, 0) +
(getGap() * (track.children.length - 1));
const half = total / dup; // one original set width
const shift = -half; // move by one set for infinite loop
const duration = Math.max(6, Math.min(half / speed, 120)); // seconds
track.style.setProperty('--lc-shift', shift + 'px');
track.style.setProperty('--lc-duration', duration + 's');
track.classList.add(dir === 'right' ? 'lc-animate-right' : 'lc-animate-left');
}
function getGap(){
const gapStr = getComputedStyle(track).gap || '0';
return parseFloat(gapStr) || 0;
}
// 3) Pause on hover (optional)
if(pauseOnHover){
root.addEventListener('mouseenter', () => root.classList.add('paused'));
root.addEventListener('mouseleave', () => root.classList.remove('paused'));
// Touch pause (tap to pause; tap outside to resume)
root.addEventListener('touchstart', () => root.classList.add('paused'), {passive:true});
document.addEventListener('touchstart', (e)=>{
if(!root.contains(e.target)) root.classList.remove('paused');
}, {passive:true});
}
// 4) Responsive recalculation
let resizeTimer;
function onResize(){
clearTimeout(resizeTimer);
resizeTimer = setTimeout(setup, 150);
}
window.addEventListener('load', setup);
window.addEventListener('resize', onResize);
window.addEventListener('orientationchange', onResize);
})();
</script>
</body>
Customize it fast
- Add logos: Replace the <img> URLs with your own (SVG or transparent PNG recommended). Keep similar heights for a clean rail.
- Speed: Change data-speed="80" (px/sec). Lower = slower; higher = faster.
- Direction: data-direction="left" or "right".
- Duplication: data-duplicate="2" usually works; bump to 3 if you have very few logos to avoid visible repeats too quickly.
- Height & spacing: tweak --lc-height and --lc-gap.
- Look: set --lc-grayscale: 1 for monochrome brand wall; adjust --lc-opacity for subtlety.
- Edges: remove .lc-fade-left/right if you prefer hard edges.
Accessibility & performance tips
- Alt text: keep concise brand names in alt attributes.
- Focus order: if logos are decorative, you can wrap them in plain <div> as shown (not links). If they should link, wrap each in <a> and ensure clear focus styles.
- Image size: serve 2× retina-ready but compressed assets; SVGs are ideal.
- Motion: you can disable hover-pause by setting data-pause-on-hover="false"; consider leaving it on for readability.
Troubleshooting
- Jumps or gaps → Increase data-duplicate so at least one full set is always off-screen while the other scrolls in.
- Too fast/slow → Adjust data-speed; the script recalculates the exact duration from total width.
- Logos look uneven → Standardize image canvas heights; keep transparent padding consistent.
- Stutters on very old devices → Lower the number of logos or reduce --lc-height and --lc-gap.