centered carousel

by kthornbloom

HTML

<div class="yea">
  <div class="yea-stage">
    <div class="yea-item">
      <img class="yea-img" src="https://images.unsplash.com/photo-1504639725590-34d0984388bd?h=300&w=200&q=80"/>
      <div class="yea-text"><b>1 Title</b><br> description goes here.</div>
    </div>
      <div class="yea-item">
      <img class="yea-img" src="https://images.unsplash.com/photo-1504639725590-34d0984388bd?h=300&w=200&q=80"/>
      <div class="yea-text"><b>2 Title</b><br> description goes here description goes here description goes here.</div>
    </div>
      <div class="yea-item">
      <img class="yea-img" src="https://images.unsplash.com/photo-1504639725590-34d0984388bd?h=300&w=200&q=80"/>
      <div class="yea-text"><b>3 Title</b><br> description goes here.</div>
    </div>
      <div class="yea-item">
      <img class="yea-img" src="https://images.unsplash.com/photo-1504639725590-34d0984388bd?h=300&w=200&q=80"/>
      <div class="yea-text"><b>4 Title</b><br> description goes here.</div>
    </div>
      <div class="yea-item">
      <img class="yea-img" src="https://images.unsplash.com/photo-1504639725590-34d0984388bd?h=300&w=200&q=80"/>
      <div class="yea-text"><b>5 Title</b><br> description goes here.</div>
    </div>
  </div>
</div>
  <button id="next">Next</button>
  <button id="prev">Prev</button>

CSS

.yea {
  position: relative;
  border: 1px solid red;
}

.yea-stage {
  border: 1px solid blue;
  display: flex;
  flex-direction: row;
  justify-content: center;
  gap: 1rem;
  max-width: 100%;
  overflow: hidden;
  padding-bottom: 100px;
}

.yea-img {
  aspect-ratio: 1.25/2;
  object-fit: cover;
  border-radius: 4px;
}

.yea-center {
    background: red;
}

.yea-item {
  position: relative;
  transition: .5s;
}

.yea-center-prev,
.yea-center-next { 
  opacity: .7;
  transform: scale(.9);
}

.yea-item:not(.yea-center):not(.yea-center-prev):not(.yea-center-next){
  opacity: .5;
  transform: scale(.7);
  margin: 0 -2em;
}

.yea-text {
  position: absolute;
  top: 100%;
  left: 0;
  width: 100%;
}

JavaScript

/**
 * Highlights center + adjacent items with smooth transitions
 * @param {number} offset - 0 = center, -1 = previous, +1 = next (only one gets the special class)
 */
function defineMiddle(offset = 0) {
  const stage = document.querySelector('.yea-stage');
  if (!stage) return;

  const items = stage.querySelectorAll('.yea-item');
  if (items.length === 0) return;

  const centerIndex = Math.floor(items.length / 2);
  const targetIndex = Math.max(0, Math.min(items.length - 1, centerIndex + offset));

  const newCenterItem = items[targetIndex];

  // Determine which class to add based on offset
  const classToAdd =
    offset === 0 ? 'yea-center' :
    offset === -1 ? 'yea-center-prev' :
    offset === 1 ? 'yea-center-next' : 'yea-center';

  // 1. Immediately add the new class → incoming animation starts now
  newCenterItem.classList.add(classToAdd);

  // 2. On next frame, remove the same class from all other items
  requestAnimationFrame(() => {
    stage.querySelectorAll('.yea-center, .yea-center-prev, .yea-center-next').forEach(el => {
      if (el !== newCenterItem) {
        el.classList.remove('yea-center', 'yea-center-prev', 'yea-center-next');
      }
    });
  });
}

defineMiddle();

const prev = document.getElementById('prev'),
      next = document.getElementById('next');



function yeaGoForward() {
   defineMiddle(-1);
  const stage = document.querySelector('.yea-stage');
  if (!stage || stage.querySelectorAll('.yea-item').length <= 1) return;

  const firstItem = stage.children[0];
  const itemWidth = firstItem.offsetWidth;

  // Step 1: Pre-apply classes for the incoming center (the current left-1 becomes center)
  // This already reflects current state
  // No need to pre-call — just let the slide happen and reassign at end

  // Step 2: Animate slide
  stage.style.transition = 'transform 0.45s ease-in-out';
  stage.style.transform = `translateX(-${itemWidth}px)`;

  // Step 3: After...