Infinite Scrolling Carousel (Css Only)

Infinite Scrolling Carousel (Css Only)

I'm trying to create a carousel which automatically plays in a loop. Initially I was in the process of implementing slick slider, but then I came across this CSS only approach:

body {
  align-items: center;
  background: #E3E3E3;
  display: flex;
  height: 100vh;
  justify-content: center;
}

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

.slider {
  background: white;
  box-shadow: 0 10px 20px -5px rgba(0, 0, 0, 0.125);
  height: 100px;
  margin: auto;
  overflow: hidden;
  position: relative;
  width: 960px;
}

.slider::before,
.slider::after {
  background: linear-gradient(to right, white 0%, rgba(255, 255, 255, 0) 100%);
  content: "";
  height: 100px;
  position: absolute;
  width: 200px;
  z-index: 2;
}

.slider::after {
  right: 0;
  top: 0;
  transform: rotateZ(180deg);
}

.slider::before {
  left: 0;
  top: 0;
}

.slider .slide-track {
  animation: scroll 40s linear infinite;
  display: flex;
  width: calc(250px * 14);
}

.slider .slide {
  height: 100px;
  width: 250px;
}
<div class="slider">
  <div class="slide-track">
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
  </div>
</div>

Now, I'm trying to reverse engineer and implement something similar for my carousel:

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

.carousel {
  padding: 100px 0;
  background: lightblue;
  overflow: hidden;
  position: relative;
}

.carousel__wrapper {
  display: flex;
  justify-content: center;
  align-items: center;
}

.carousel__slide {
  animation: scroll 10s linear infinite;
  display: flex;
  flex-direction: column;
  width: 33%;
}

.carousel__image {
  background-size: cover;
  background-repeat: no-repeat;
  height: 100px;
  width: 100px;
  margin: 15px 20px;
}
<link href="" rel="stylesheet">

<section class="carousel">

  <div class="container-fluid px-0">
    <div class="row">
      <div class="col-12">
        <div class="carousel__wrapper">

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');"></div>
            <div class="carousel__image" style="background-image: url(');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');"></div>
            <div class="carousel__image" style="background-image: url(');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');"></div>
            <div class="carousel__image" style="background-image: url(');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');"></div>
            <div class="carousel__image" style="background-image: url(');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');"></div>
            <div class="carousel__image" style="background-image: url(');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');"></div>
            <div class="carousel__image" style="background-image: url(');"></div>
          </div>

        </div>
      </div>
    </div>
  </div>

</section>

In my demo, at the end of the carousel there's blank space. Whereas in the working demo above, the logos loop smoothly (starts from the beginning without the blank space).

Now, in the working demo above, I stripped back all the CSS to only this:

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

.slide-track {
  animation: scroll 10s linear infinite;
  display: flex;
}
<div class="slider">
  <div class="slide-track">
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="" height="100" width="250" alt="" />
    </div>
  </div>
</div>

The above is working, and the css from above exists on my demo, but it doesn't work as smoothly? How can I make the transition (make it start from the beginning) after the last slide is shown?

1 Answer

The carousel works based on fixed widths. If there are 7 slides:

  1. They've duplicated slides once in the html. Make sure you repeat slides.
  2. The carousel__wrapper has given width: calc(250px * 14);. Twice the number of slides to show. Note, the wrapper is dependent on slide width and slides are not dependent on wrapper.
  3. The carousel has width less than the wrapper it's 250px * 4 and the overflow is hidden so we see only the window and not entire carousel__wrapper.
  4. The animation shifts the slides by calc(-250px * 7) to left. Here, 250px is slide width. Note they are shifting only by 7 slides not entire 14.

You can't use relative dimensions without using javascript. To keep it CSS only you'll need absolute widths.
If you use variables then things will be easy to maintain and understand:

:root {
  --no-of-slides: 6;
  --slides-in-view: 4;
  --slide-width: 200px;
  --slide-height: 300px;
  --iteration-time: 10s;
}

@keyframes scroll {
  0% {
    transform: translateX(0);
  }
  100% {
    transform: translateX(calc(var(--slide-width) * var(--no-of-slides)* -1));
  }
}

.carousel__wrapper {
  display: flex;
  /*justify-content: center;*/
  align-items: center;
  
  width: calc(var(--slides-in-view) * var(--slide-width));
  overflow: hidden;
  border: 1px dashed gray;
  margin: 0 auto;
}

.carousel {
  padding: 100px 0;
  background: lightblue;
  
  overflow: hidden;
  width: calc(2 * var(--no-of-slides));
}

.carousel__slide {
  animation: scroll var(--iteration-time) linear infinite;
  display: flex;
  flex-direction: column;
  
  flex: 0 0 auto;
  width: var(--slide-width);
  height: var(--slide-height);
  box-sizing: border-box;
  /*border: 1px dotted darkblue;*/
}

.carousel__image {
  background-size: cover;
  background-repeat: no-repeat;
  
  height: 50%;
  /*width: 100px;*/
  margin: 15px 20px;
}


/* just for analysis remove this 3 rules later*/
    .carousel__slide {
      position: relative;
    }

    .carousel {
      counter-reset: slideNo;
    }

    .carousel__slide::before {
      counter-increment: slideNo;
      content: counter(slideNo);
      position: absolute;
      top: 0%;
      left: 50%;
      font-size: 2rem;
      color: lime;
    }
<link href="" rel="stylesheet">

<section class="carousel">

  <div class="container-fluid px-0">
    <div class="row">
      <div class="col-12">
        <div class="carousel__wrapper">

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');"></div>
            <div class="carousel__image" style="background-image: url(');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');"></div>
            <div class="carousel__image" style="background-image: url(');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');"></div>
            <div class="carousel__image" style="background-image: url(');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');"></div>
            <div class="carousel__image" style="background-image: url(');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');"></div>
            <div class="carousel__image" style="background-image: url(');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');">
            </div>
            <div class="carousel__image" style="background-image: url(');">
            </div>
          </div>

          <!--#### repeat ####-->
          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');"></div>
            <div class="carousel__image" style="background-image: url(');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');"></div>
            <div class="carousel__image" style="background-image: url(');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');"></div>
            <div class="carousel__image" style="background-image: url(');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');"></div>
            <div class="carousel__image" style="background-image: url(');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');"></div>
            <div class="carousel__image" style="background-image: url(');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');">
            </div>
            <div class="carousel__image" style="background-image: url(');">
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</section>

In above CSS rules my changes are after blank lines. See the comments in the code.

If you want dimensions based on viewport then you can use vw and vh units:

:root {
  --no-of-slides: 6;
  --slides-in-view: 4;
  --slide-width: 33vw;
  --slide-height: 50vh;
  --iteration-time: 10s;
}

@keyframes scroll {
  0% {
    transform: translateX(0);
  }
  100% {
    transform: translateX(calc(var(--slide-width) * var(--no-of-slides)* -1));
  }
}

.carousel__wrapper {
  display: flex;
  /*justify-content: center;*/
  align-items: center;
  
  width: calc(var(--slides-in-view) * var(--slide-width));
  overflow: hidden;
  border: 1px dashed gray;
  margin: 0 auto;
}

.carousel {
  padding: 10px 0;
  background: lightblue;
  
  overflow: hidden;
  width: calc(2 * var(--no-of-slides));
}

.carousel__slide {
  animation: scroll var(--iteration-time) linear infinite;
  display: flex;
  flex-direction: column;
  
  flex: 0 0 auto;
  width: var(--slide-width);
  height: var(--slide-height);
  box-sizing: border-box;
  /*border: 1px dotted darkblue;*/
}

.carousel__image {
  background-size: cover;
  background-repeat: no-repeat;
  
  height: 50%;
  /*width: 100px;*/
  margin: 15px 20px;
}


/* just for analysis remove this 3 rules later*/
    .carousel__slide {
      position: relative;
    }

    .carousel {
      counter-reset: slideNo;
    }

    .carousel__slide::before {
      counter-increment: slideNo;
      content: counter(slideNo);
      position: absolute;
      top: 0%;
      left: 50%;
      font-size: 2rem;
      color: lime;
    }
<link href="" rel="stylesheet">

<section class="carousel">

  <div class="container-fluid px-0">
    <div class="row">
      <div class="col-12">
        <div class="carousel__wrapper">

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');"></div>
            <div class="carousel__image" style="background-image: url(');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');"></div>
            <div class="carousel__image" style="background-image: url(');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');"></div>
            <div class="carousel__image" style="background-image: url(');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');"></div>
            <div class="carousel__image" style="background-image: url(');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');"></div>
            <div class="carousel__image" style="background-image: url(');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');">
            </div>
            <div class="carousel__image" style="background-image: url(');">
            </div>
          </div>

          <!--#### repeat ####-->
          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');"></div>
            <div class="carousel__image" style="background-image: url(');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');"></div>
            <div class="carousel__image" style="background-image: url(');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');"></div>
            <div class="carousel__image" style="background-image: url(');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');"></div>
            <div class="carousel__image" style="background-image: url(');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');"></div>
            <div class="carousel__image" style="background-image: url(');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url(');">
            </div>
            <div class="carousel__image" style="background-image: url(');">
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</section>
1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Sarah Jenkins
Author

Sarah Jenkins

Sarah Jenkins is a veteran tech journalist with over 12 years of experience covering artificial intelligence, mobile innovations, and digital ethics. Her insights have appeared in leading technology publications worldwide.