Making an infinite CSS carousel
Tags: carousel, css, html, nojavascript
Published: 2021-04-11 15:25:51

---
title: Making an infinite CSS carousel 
published: true
description: How to make a carousel that doesn't require javascript
tags: carousel, css, html, nojavascript
---

After searching and struggling for many hours, I still couldn't find a good guide on how to make an infinite CSS carousel. There's no reason this can't be done using html and css, and yet almost every guide is using javascript, or css that doesn't actually work when you try it.

It's so simple and yet a working example is just not available, so I'm sharing what we've done at [Rhosys](https://rhosys.ch) to make our brand carousel.

And of course you want to see the finished product first:
![rhosys-brand-carousel](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/khmxzkc9oov551wz1lif.gif)
 

## Diving into the code
Here's our simple display:

```html
<section class="user-cloud">
  <div class="brands-container">
    <div class="brands-carousel">
      <picture>
        <source srcset="assets/images/s-customer-cloud1.png"
            media="(max-width: 766px)" />
        <img src="assets/images/customer-cloud1.png" />
      </picture>
      <picture>
        <source srcset="assets/images/s-customer-cloud2.png"
            media="(max-width: 766px)" />
        <img src="assets/images/customer-cloud2.png" />
      </picture>
      <picture>
        <source srcset="assets/images/s-customer-cloud3.png"
            media="(max-width: 766px)" />
        <img src="assets/images/customer-cloud3.png" />
      </picture>
      <picture>
        <source srcset="assets/images/s-customer-cloud4.png"
            media="(max-width: 766px)" />
        <img src="assets/images/s-customer-cloud4.png" />
      </picture>
    </div>
  </div>
</section>
```

We have four images on mobile and only three on desktop. To make this responsive we are using the `picture` tag with `source` and `img` set. Img selectors didn't work, so we use picture. No idea why, but rather than fighting with that it's easier to do this. No messy `x2 multiples` or figuring out what the size of the elements should be on the screen.

Then we'll add some nice padding and setup to our container.

**IMPORTANT**: the `max-width` here should always been the same width as all your pictures, so that 100% means the full picture width:
```css
.brands-container {
  /* all pictures should be the same size as this value. */
  max-width: 1050px;
  margin: auto;
  padding:0 1em;
  overflow: hidden;
}

.brands-carousel {
  position: relative;
  padding-left: 0;
  margin: 0;
  height: 200px;
  overflow: hidden;
}

.brands-carousel > div {
  width: 100%;
}
```

That's the setup, which is relatively simple, and here's the important setup:

```css
/* Each picture in the carousel is 100% of the parent. */
.brands-carousel > picture {
  width: 100%;
  position: absolute;
  top: 0;
  display: flex;
  justify-content: center;
  animation: carousel 20s linear infinite;

  /* It also starts off the screen until it is time. */
  transform: translateX(100%);
}
```

(I'm going to skip talking about the `first-picture` keyframe for a second)
Every picture get's the same setup, it takes N seconds to move onto the screen and stay there until the next picture moves. Calculated on desktop there are three pictures, so each one gets 1/3 of the time on stage.

```css
.brands-carousel > picture:nth-child(1) {
  animation-name: first-picture, carousel;
  animation-duration: 20s;
  animation-iteration-count: 1, infinite;
  animation-delay: 0s, 20s;
  transform: translateX(0%);
}
.brands-carousel > picture:nth-child(2) {
  animation-delay: Calc(20s * .33);
}
.brands-carousel > picture:nth-child(3) {
  animation-delay: Calc(20s * .66);
}
```

```css
/* The keyframes for desktop */
@keyframes first-picture {
  0% { transform: translateX(0%); }
  7.5%, 33% { transform: translateX(0); }
  40.5%, 100% { transform: translateX(-100%); }
}

@keyframes carousel {
  0% { transform: translateX(100%); }
  7.5%, 33% { transform: translateX(0); }
  40.5%, 100% { transform: translateX(-100%); }
}
```

So the main keyframe is carousel. Since each image is on stage for 1/3 of the time, It will slide in taking 7.5% of the 20s time to do that, and stay there until the end of it's 33%, and which time it transfers out. Since each image takes 7.5% to enter, it also has to take 7.5% to leave.
`33% + 7.5% = 40.5`.

And this almost totally works, except for one thing, until the 33% of 20s no image is fully displayed. The fix for this is a hack which shows the first image two times. We'll show it on the screen to start until it leaves and at the same time we'll show it off the screen to the right until it starts. We'll then delay the second animation one full round. Because of this, we need the `first-picture` keyframe, and this works great.

```css
/* The keyframes for mobile */
@keyframes first-picture-responsive {
  0% { transform: translateX(0%); }
  5.5%, 25% { transform: translateX(0); }
  30.5%, 100% { transform: translateX(-100%); }
}

@keyframes carousel-responsive {
  0% { transform: translateX(100%); }
  5.5%, 25% { transform: translateX(0); }
  30.5%, 100% { transform: translateX(-100%); }
}
```

Don't show the forth picture on desktop
```css
.brands-carousel > picture:last-child {
  display: none;
}
```

On mobile we'll make some small adjustments, instead of 20s for 3, well have 27s for 4 images, each image gets 1/4 of the time on stage.

```css
@media screen and (max-width: 766px) {
  .brands-carousel > picture {
    animation: carousel-responsive 27s linear infinite;
  }

  .brands-carousel > picture:nth-child(1) {
    animation-name: first-picture-responsive, carousel-responsive;
    animation-duration: 27s;
    animation-iteration-count: 1, infinite;
    animation-delay: 0s, 27s;
  }

  .brands-carousel > picture:nth-child(2) {
    animation-delay: Calc(27s * .25);
  }
  .brands-carousel > picture:nth-child(3) {
    animation-delay: Calc(27s * .50);
  }
  .brands-carousel > picture:nth-child(4) {
    animation-delay: Calc(27s * .75);
    display: block;
  }
}
```

### Finishing up
If you want to change the time of the full animate loop replace the `20s` with your new full time. To change how long a transition is on the screen reduce the `7.5%` to a smaller value (and reduce the `40.5%` by the same amount). To make any other change (i.e. increasing the length of time the image is static, you'll need to compute that based on 33% of the total time and then recalculate the transition percentage.

Right now the static image is 7.5% to 33% that's 25.5% of 20s (`5.1s`) on the screen. If you want that to be `6s` on the screen without reducing the transition time (`7.5% * 20s = 1.5s`). Calculate the total new time `6 / .255 = 23.5s` for the full animation and then new transition percentages are `1.5s / 23.5s = 6.4%` so the new keyframes would be

```css
/* # Updating all the 20s => 23.5s */
@keyframes carousel {
  0% { transform: translateX(100%); }
  6.4%, 33% { transform: translateX(0); }
  39.4%, 100% { transform: translateX(-100%); }
}
```
And that's it. 

Here's a [link to the code](https://codepen.io/wparad/pen/rNjJeqM)