Fast and accessible CSS-only client logo carousel

on in Blog
Last modified on

Client Logo Carousel
Photo by Dan Lazar on Unsplash

Your latest project needs a client logo carousel. One in two sites requires some kind of logo carousel, either client logos, or provider logos.

In order to keep Google’s CLS metric in check and also load the carousel instantly, here’s the fastest solution. This logo carousel works with JPEG, PNG, SVG, WebP and GIF images. It also works with text, or other HTML elements, provided you set a fixed slide width.

There are 2 tricks you need to remember:

  1. Have 2 identical sets of logos.
  2. Learn the formula.

Let’s break it down.

The HTML structure is simple. We have the main slider element, with a track inside and a set of images:

Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
<div class="slider">
    <div class="slide-track-1">
        <div class="slide">
            <img src="/path/to/image1.png" alt="">
        </div>
        <div class="slide">
            <img src="/path/to/image2.png" alt="">
        </div>
    </div>
</div>

Add as many images as you want, then add them again, like below:

<div class="slider">
    <div class="slide-track-1">
        <div class="slide">
            <img src="/path/to/image1.png" alt="">
        </div>
        <div class="slide">
            <img src="/path/to/image2.png" alt="">
        </div>

        <div class="slide">
            <img src="/path/to/image1.png" alt="">
        </div>
        <div class="slide">
            <img src="/path/to/image2.png" alt="">
        </div>
    </div>
</div>

The rest is CSS, which again, is very, very simple. We’ll also compress it, just to prove a point :)

.slider {
    background: #efeef2;
    height: 75pt;
    margin: 0 auto;
    overflow: hidden;
    position: relative;
    cursor: default;
    user-select: none;
    touch-action: none;
}

.slider:before {
    left: 0;
    top: 0;
}
.slider:after,
.slider:before {
    background: linear-gradient(
        to right,
        #efeef2 0%,
        hsla(0, 0%, 100%, 0) 100%
    );
    content: "";
    height: 75pt;
    position: absolute;
    width: 200px;
    z-index: 2;
}
.slider:after {
    right: 0;
    top: 0;
    transform: rotateZ(180deg);
}
.slider:after,
.slider:before {
    background: linear-gradient(
        to right,
        #efeef2 0%,
        hsla(0, 0%, 100%, 0) 100%
    );
    content: "";
    height: 75pt;
    position: absolute;
    width: 200px;
    z-index: 2;
}

.slider .slide-track-1 {
    animation: scroll-l 30s linear infinite;
    display: flex;
    width: 3500px;
}
.slider .slide-track-2 {
    animation: scroll-r 30s linear infinite;
    display: flex;
    width: 3500px;
}

.slider .slide {
    height: 75pt;
    width: 250px;
    display: flex;
    align-items: center;
    text-align: center;
}
.slider .slide img {
    width: 180px;
    padding: 1pc;
    vertical-align: middle;
    margin: 0 auto;
    display: inline-block;
    max-width: 100%;
    height: auto;
}

@keyframes scroll-l {
    0% {
        transform: translateX(0);
    }
    100% {
        transform: translateX(calc(-250px * 7));
    }
}
@keyframes scroll-r {
    100% {
        transform: translateX(0);
    }
    0% {
        transform: translateX(calc(-250px * 7));
    }
}

Minified and Gzipped is 454 bytes.

.slider{background:#efeef2;height:75pt;margin:0 auto;overflow:hidden;position:relative;cursor:default;user-select:none;touch-action:none}.slider:before{left:0;top:0}.slider:after{right:0;top:0;transform:rotateZ(180deg)}.slider:after,.slider:before{background:linear-gradient(to right,#efeef2 0,hsla(0,0%,100%,0) 100%);content:"";height:75pt;position:absolute;width:200px;z-index:2}.slider .slide-track-1{animation:scroll-l 30s linear infinite;display:flex;width:3500px}.slider .slide-track-2{animation:scroll-r 30s linear infinite;display:flex;width:3500px}.slider .slide{height:75pt;width:250px;display:flex;align-items:center;text-align:center}.slider .slide img{width:180px;padding:1pc;vertical-align:middle;margin:0 auto;display:inline-block;max-width:100%;height:auto}@keyframes scroll-l{0%{transform:translateX(0)}100%{transform:translateX(calc(-250px * 7))}}@keyframes scroll-r{100%{transform:translateX(0)}0%{transform:translateX(calc(-250px * 7))}}

In our example above, note the slide width – 250px – and the total slider track width – 3500px. Our example has 7 images. That is:

7 × 250 = 1750

We also have 2 sets of images, so:

1750 × 2 = 3500

That’s it! Assuming you’d have 10 logos and keeping the 250px width, the total track width would be 5000px:

(10 × 250) × 2 = 5000

Next, let’s make Google happy by lazy loading the images. In most cases, logo carousels are below the fold, so the images don’t need to be loaded. Take a note of your logos’ width and height (they should all be the same) and add these parameters to each image element:

width="300" height="240" alt="Logo description" loading="lazy"

The resulting slide should look like below:

...

<div class="slide">
    <img src="/path/to/1.png" width="300" height="240" alt="Logo description" loading="lazy">
</div>

...

See some examples below:

Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description
Logo description

Related posts