Fast and accessible JavaScript client logo carousel

on in JavaScript Carousels
Last modified on

Client Logo Carousel

A while ago, I had to create a client logo carousel using CSS only. I did, and it worked perfectly.

I tried to use it on several other projects after that, and I always got lost in calculating the width of the carousel container based on the number of slides. So, if the carousel was dynamic, and new logos were being added by a CMS, then I was out of luck. Also, as the logos were managed by the client, there was no way for them to adjust the CSS.

I then looked into JavaScript. I wanted some simple, inline code, instead of having an external dependency. This is how the logo carousel below was created.

I’m trying to keep everything simple and accessible, so I am using the role="region" attribute to define a region, a distinct section of content.

Also, for accessibility purposes, I need to provide a descriptive label for the carousel using the aria-label attribute. Considering the carousel contains a list of items, I am marking them accordingly using the role="list" and role="listitem" attributes.

<div class="carousel" id="carousel" role="region" aria-label="Logo Carousel">
    <div class="carousel-container" id="carouselContainer" role="list">
        <div class="carousel-item" role="listitem">Logo 1</div>
        <div class="carousel-item" role="listitem">Logo 2</div>
        <div class="carousel-item" role="listitem">Logo 3</div>
        <div class="carousel-item" role="listitem">Logo 4</div>
        <div class="carousel-item" role="listitem">Logo 5</div>
        <div class="carousel-item" role="listitem">Logo 6</div>
    </div>
</div>

That’s all you require for the structure. Add as many images (or elements) as you want.

Next, let’s style it.

I used a CSS variable to set the number of items (logos, elements, etc.) on the page. One caveat is that you need to have at least this number of elements. For example, if you have 4 logos, and you want 5 items per page, you will get 5 (the 5th one being an empty gap).

However, from experience, logo carousels have at least 4 or 5 logos, so we are safe. Note that if you are using images, instead of the text I used in the demo version, you will need to make sure they are the same height.

:root {
    --items-per-page: 4;
}

.carousel {
    width: 100%;
    overflow: hidden;
    position: relative;
}

.carousel-container {
    /* Prevent logos from wrapping to the next line */
    white-space: nowrap;
}

.carousel-item {
    display: inline-block;
    min-width: calc(100% / var(--items-per-page));
    box-sizing: border-box;
    padding: 20px;
    text-align: center;
    background-color: #f2f2f2;
    border: 1px solid #ccc;
}

The last step is adding the JavaScript code. I switched from using setInterval() to requestAnimationFrame() in order to use 60fps and a smooth motion. The code is minimal.

const carouselContainer = document.getElementById('carouselContainer');

// Clone the carousel content to create a continuous loop
const carouselItems = carouselContainer.innerHTML;
carouselContainer.innerHTML += carouselItems;

// Set up animation
let scrollLeft = 0;
const scrollSpeed = 4; // Adjust the scroll speed as needed

function animateCarousel(timestamp) {
    if (!lastTimestamp) {
        lastTimestamp = timestamp;
    }

    const deltaTime = timestamp - lastTimestamp;
    lastTimestamp = timestamp;

    scrollLeft += scrollSpeed * deltaTime / 60; // Normalize speed
    if (scrollLeft >= carouselContainer.scrollWidth / 2) {
        scrollLeft = 0;
    }
    carouselContainer.style.transform = `translateX(-${scrollLeft}px)`;

    requestAnimationFrame(animateCarousel);
}

let lastTimestamp = null;
requestAnimationFrame(animateCarousel);

If you feel like it, the script above can be minified and inlined, right below the carousel. I am not doing that, though, as I have an additional caching layer that also compresses inline scripts.

const carouselContainer=document.getElementById("carouselContainer"),carouselItems=carouselContainer.innerHTML;carouselContainer.innerHTML+=carouselItems;let scrollLeft=0;const scrollSpeed=4;function animateCarousel(e){lastTimestamp||(lastTimestamp=e);const t=e-lastTimestamp;lastTimestamp=e,scrollLeft+=4*t/60,scrollLeft>=carouselContainer.scrollWidth/2&&(scrollLeft=0),carouselContainer.style.transform=`translateX(-${scrollLeft}px)`,requestAnimationFrame(animateCarousel)}let lastTimestamp=null;requestAnimationFrame(animateCarousel);

That is all. A minimal, zero-footprint, 60 frames-per-second, smooth, performant carousel.

See the Pen Fast and accessible JavaScript client logo carousel by Ciprian (@ciprian) on CodePen.

Related posts