Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Infinite scrolling carousel (CSS only)

Tags:

html

css

sass

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="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/1.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/2.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/3.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/4.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/5.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/6.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/7.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/1.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/2.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/3.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/4.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/5.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/6.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/7.png" 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="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" 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('https://picsum.photos/200');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/200');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/200');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/200');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/200');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/200');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/200');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/200');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/200');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/200');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/200');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/200');"></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="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/1.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/2.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/3.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/4.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/5.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/6.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/7.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/1.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/2.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/3.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/4.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/5.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/6.png" height="100" width="250" alt="" />
    </div>
    <div class="slide">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/557257/7.png" 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?

like image 556
Freddy Avatar asked Mar 26 '26 11:03

Freddy


1 Answers

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="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" 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('https://picsum.photos/seed/1/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/1/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/2/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/2/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/3/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/3/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/4/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/4/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/5/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/5/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/picsum/200/300');">
            </div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/picsum/200/300');">
            </div>
          </div>

          <!--#### repeat ####-->
          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/1/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/1/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/2/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/2/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/3/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/3/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/4/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/4/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/5/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/5/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/picsum/200/300');">
            </div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/picsum/200/300');">
            </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="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" 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('https://picsum.photos/seed/1/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/1/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/2/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/2/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/3/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/3/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/4/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/4/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/5/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/5/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/picsum/200/300');">
            </div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/picsum/200/300');">
            </div>
          </div>

          <!--#### repeat ####-->
          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/1/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/1/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/2/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/2/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/3/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/3/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/4/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/4/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/5/200/300');"></div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/5/200/300');"></div>
          </div>

          <div class="carousel__slide">
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/picsum/200/300');">
            </div>
            <div class="carousel__image" style="background-image: url('https://picsum.photos/seed/picsum/200/300');">
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</section>
like image 158
the Hutt Avatar answered Mar 28 '26 03:03

the Hutt



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!