<div class="vegetables">
  <div class="inner">
    <ul class="page">
      <li><span>Carrot</span></li>
      <li><span>Tomato</span></li>
      <li><span>Broccoli</span></li>
      <li><span>Cucumber</span></li>
      <li><span>Spinach</span></li>
    </ul>
    <!-- Page 1 -->

    <ul class="page">
      <li><span>Potato</span></li>
      <li><span>Pepper</span></li>
      <li><span>Eggplant</span></li>
      <li><span>Lettuce</span></li>
      <li><span>Onion</span></li>
    </ul>
    <!-- Page 2 -->

    <ul class="page">
      <li><span>Zucchini</span></li>
      <li><span>Cauliflower</span></li>
      <li><span>Kale</span></li>
      <li><span>Cabbage</span></li>
      <li><span>Mushroom</span></li>
    </ul>
    <!-- Page 3 -->

    <ul class="page">
      <li><span>Asparagus</span></li>
      <li><span>Artichoke</span></li>
      <li><span>Radish</span></li>
      <li><span>Beet</span></li>
      <li><span>Okra</span></li>
    </ul>
    <!-- Page 4 -->

    <ul class="page">
      <li><span>Brussels Sprouts</span></li>
      <li><span>Green Bean</span></li>
      <li><span>Butternut Squash</span></li>
      <li><span>Turnip</span></li>
      <li><span>Chard</span></li>
    </ul>
    <!-- Page 5 -->
  </div>
</div>
<div class="pagination">
  <ul id="dots">
    <li class="prev"></span></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <svg width="430" height="200" viewBox="0 0 400 200">
      <path></path>
    </svg>
  </ul>
  <button id="prevPage">Previous</button>
  <button id="nextPage">Next</button>
</div>
@font-face {
  font-family: "Romie";
  src: url("https://assets.codepen.io/383755/Romie-Regular.woff2")
    format("woff2");
}

body {
  font-family: sans-serif;
  max-width: 100vw;
  height: 100vh;
  position: relative;
  overflow: hidden;
  --black: #151515;
  --c1: #0272f2;
  --c2: #ff5213;
  --c3: #ffdf3f;
  --c4: #01b26e;
  --c5: #fea9e0;
  --c6: #ffdf3f;
  background: var(--black);
}

.vegetables {
  perspective: 500px;
  transform-style: preserve-3d;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  width: 100vw;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
  margin: 0 auto;
  font-family: "Romie";
  * {
    transform-style: preserve-3d;
  }
  .inner {
    position: absolute;
    width: 100vw;
    height: 100vh;
  }
}

.page {
  transform-style: preserve-3d;
  position: absolute;
  top: calc(50% - 100px);
  left: 50%;
  list-style: none;
  padding: 0;
  height: calc(90vmin - 300px);
  width: 90vmin;
  display: flex;
  justify-content: center;
  align-items: stretch;
  flex-wrap: wrap;
  gap: 10px;
  padding: 0;
  margin: 0;
  transform: translate(-50%, -50%) translateZ(200vmin);
  transition: 0.3s cubic-bezier(0.175, 0.885, 0.32, 1) 0.75s;
  li {
    transition: 0.5s ease-in-out;
    opacity: 0;
    font-size: 7vmin;
    @for $i from 1 through 5 {
      &:nth-of-type(#{$i}) {
        color: #ccc;
        span:after {
          transform: scaleX(0);
          background: #ccc;
          transition: 0.5s ease-in-out #{($i/8) + 0.75}s;
        }
      }
    }
  }
  &.current {
    transform: translate(-50%, -50%) translateZ(0vmin);
    li {
      opacity: 1;
      background-position: 50% 100%;
      @for $i from 1 through 5 {
        &:nth-of-type(#{$i}) {
          transition: 0.5s ease-in-out 0.75s, color 0.5s ease-in-out #{($i/8) + 1}s;
          color: var(--c#{$i});
          span:after {
            transform: scaleX(1);
            background: var(--c#{$i});
            transition: 0.5s ease-in-out #{($i/8) + 0.75}s;
          }
        }
      }
    }

    &:nth-of-type(even) li {
      @for $i from 1 through 5 {
        &:nth-of-type(#{$i}) {
          color: var(--c#{6 - $i});
          span:after {
            background: var(--c#{6 -$i});
          }
        }
      }
    }
  }
}

.page li {
  display: flex;
  justify-content: flex-start;
  align-items: center;
  width: 100%;
  overflow: hidden;
  span {
    display: inline-block;
    position: relative;
    background: var(--black);
    &:after {
      content: "";
      position: absolute;
      width: 100vw;
      left: calc(100% + 1rem);
      top: calc(50% - 0.5px);
      height: 1px;
      background: #ccc;
      z-index: -1;
    }
  }
  &:nth-of-type(even) {
    justify-content: flex-end;
    span {
      &:after {
        left: auto;
        right: calc(100% + 1rem);
      }
    }
  }
}

.pagination {
  text-align: center;
  margin-top: 20px;
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  padding: 1rem;
}

button {
  padding: 8px 16px;
  border: 1px solid #ccc;
  background: transparent;
  color: #ccc;
  cursor: pointer;
  margin: 0 7.5px 1rem;
  position: relative;
  z-index: 999;
  width: 200px;
  border-radius: 50px;
  transition: 0.3s ease-in-out;
  font-family: "Romie";
  font-size: 1.15rem;
  padding: 0.15rem;
  font-weight: 900;
}

button:hover {
  border-color: var(--c3);
  color: var(--c3);
}

button:disabled {
  border-color: #666;
  color: #666;
  cursor: not-allowed;
}

#dots {
  --timing: 0.75;
  width: 400px;
  list-style-type: none;
  position: absolute;
  top: -150px;
  height: 200px;
  left: 50%;
  transform: translate(-50%);
  padding: 0;
  margin: 0;
  --basepath: "M 0 100 Q 200 100 400 100 ";
  --basepath-1: "M 0 150 Q 100 100 400 100 ";
  --basepath-2: "M 0 100 Q 100 200 400 100 ";
  --basepath-3: "M 0 100 Q 200 200 400 100 ";
  --basepath-4: "M 10 100 Q 300 200 400 100 ";
  --basepath-5: "M 10 100 Q 300 100 400 150 ";
  @for $i from 1 through 5 {
    --placement-#{$i}: #{(($i - 1) * 100) - 5}px;
    &:has(li:nth-of-type(#{$i}).active) {
      @for $j from 1 through 5 {
        &:has(li:nth-of-type(#{$j}).prev) {
          --origin: var(--placement-#{$j});
          --destination: var(--placement-#{$i});
          svg path {
            animation: spring#{$j} 0.75s ease-in-out 1 forwards;
            @keyframes spring#{$j} {
              75% {
                d: path(var(--basepath-#{$j}));
                animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.875);
              }
              100% {
                d: path(var(--basepath));
                animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.875);
              }
            }
          }
          li {
            animation: bounce#{$j} 0.75s ease-in-out 1 forwards;
            @keyframes bounce#{$j} {
              75% {
                offset-path: path(var(--basepath-#{$j}));
                animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.875);
              }
              100% {
                offset-path: path(var(--basepath));
                animation-timing-function: cubic-bezier(0.175, 0.885, 0.32, 1.875);
              }
            }
            &:before {
              animation: shift#{$j}
                  calc(0.35s * var(--timing))
                  ease-out
                  1
                  forwards
                  0.6s,
                jump#{$j} calc(0.35s * var(--timing)) linear 1 forwards 0.6s;
              @keyframes shift#{$j} {
                100% {
                  transform: translateX(calc(var(--destination) - var(--origin)));
                }
              }
              @keyframes jump#{$j} {
                25%,
                75% {
                  top: calc(-50px * (var(--timing) * 0.75));
                }
                50% {
                  top: calc(-50px * (var(--timing) * 1.25));
                }
                33%,
                66% {
                  top: calc(-50px * (var(--timing) * 1));
                }
              }
            }
          }
        }
      }
    }
  }
}

svg {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: -1;
  stroke-linecap: round;
  filter: drop-shadow(0 5px 10px rgba(0, 0, 0, 0.25));
  path {
    stroke-width: 25px;
    stroke: var(--c1);
    fill: none;
    d: path(var(--basepath));
  }
}

#dots li {
  width: 10px;
  height: 10px;
  background: var(--black);
  box-shadow: 0 0 0 3px var(--black);
  border-radius: 100%;
  offset-path: path(var(--basepath));
  position: absolute;
  cursor: pointer;
  &:hover {
    box-shadow: 0 0 0 2px var(--black), 0 0 0 3px var(--c5);
  }
  &:before {
    content: "";
    position: absolute;
    width: 100%;
    height: 100%;
    background: var(--c5);
    left: 0;
    top: 0;
    border-radius: 100%;
    opacity: 0;
    z-index: 999;
    pointer-events: none;
  }
  &.active {
    pointer-events: none;
  }
  &.prev {
    z-index: 9;
    &:before {
      opacity: 1;
    }
  }
  @for $i from 1 through 5 {
    &:nth-of-type(#{$i}) {
      offset-distance: calc(#{($i - 1) * 25} * 1%);
    }
  }
}
View Compiled
document.addEventListener("DOMContentLoaded", function () {
  const pages = document.querySelectorAll(".page");
  const prevButton = document.getElementById("prevPage");
  const nextButton = document.getElementById("nextPage");
  let currentPage = 0;

  function showPage(pageNumber) {
    pages.forEach((page, index) => {
      page.classList.remove("current");
      pages[pageNumber].classList.add("current");
      document.body.style.setProperty("--shift", pageNumber);
    });
  }

  function updateButtons() {
    prevButton.disabled = currentPage === 0;
    nextButton.disabled = currentPage === pages.length - 1;
  }

  prevButton.addEventListener("click", function () {
    if (currentPage > 0) {
      currentPage--;
      showPage(currentPage);
      updateButtons();
      document.querySelectorAll("#dots li")[currentPage].click();
    }
  });

  nextButton.addEventListener("click", function () {
    if (currentPage < pages.length - 1) {
      currentPage++;
      showPage(currentPage);
      updateButtons();
      document.querySelectorAll("#dots li")[currentPage].click();
    }
  });

  showPage(currentPage);
  updateButtons();

  let dotNav = document.getElementById("dots"),
    newIndex = 0,
    prevIndex;

  document.querySelectorAll("#dots li").forEach((dot, index) => {
    dot.addEventListener("click", function (e) {
      currentPage = index;
      showPage(currentPage);
      if (document.getElementsByClassName("active")[0]) {
        prevIndex = newIndex;
        newIndex = index;
        updateButtons();
        dotNav.style.setProperty(
          "--timing",
          Math.abs((newIndex - prevIndex) / 5) + 0.5
        );
        document.getElementsByClassName("prev")[0] &&
          document.getElementsByClassName("prev")[0].classList.remove("prev");
        document.getElementsByClassName("active")[0].classList.add("prev");
        document.getElementsByClassName("active")[0].classList.remove("active");
      }
      e.target.classList.add("active");
    });
  });
});

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.