CSS | Step Progress Transition

by Zoltan Boros

HTML

<div class="steps">
  <div class="step" id="step1">
    <div class="background completed"></div>
    <div class="background pending"></div>
    <div class="background upcoming"></div>
    Step 1
  </div>
  <div class="step" id="step2">
    <div class="background completed"></div>
    <div class="background pending"></div>
    <div class="background upcoming"></div>
    Step 2
  </div>
  <div class="step" id="step3">
    <div class="background completed"></div>
    <div class="background pending"></div>
    <div class="background upcoming"></div>
    Step 3
  </div>
  <div class="step" id="step4">
    <div class="background completed"></div>
    <div class="background pending"></div>
    <div class="background upcoming"></div>
    Step 4
  </div>
  <div class="step" id="step5">
    <div class="background completed"></div>
    <div class="background pending"></div>
    <div class="background upcoming"></div>
    Step 5
  </div>
</div>
<button type="button" id="back">back</button>
<button type="button" id="next">next</button>

CSS

.steps {
  margin-bottom: 1rem;
  display: flex;
  align-items: felx-start;
}
.step {
  position: relative;
  flex-grow: 1;
  overflow: hidden;
  margin: 1px;
  text-align: center;
}
.step .background {
  position: absolute;
  width: 100%;
  height: 100%;
  z-index: -1;
  transition: left 0.75s;
}

.step .background.completed {
  background-color: #dfd;
}
.step .background.pending {
  background-color: #ddf;
}
.step .background.upcoming {
  background-color: #fdd;
}

.step.completed .background.completed {
  left: 0;
}
.step.completed .background.pending {
  left: 100%;
}
.step.completed .background.upcoming {
  left: 200%;
}

.step.pending .background.completed {
  left: -100;
}
.step.pending .background.pending {
  left: 0;
}
.step.pending .background.upcoming {
  left: 100%;
}

.step.upcoming .background.completed {
  left: -200%;
}
.step.upcoming .background.pending {
  left: -100%;
}
.step.upcoming .background.upcoming {
  left: 0;
}

JavaScript

const statusClasses = [ 'completed', 'pending', 'upcoming' ];

const steps = [];
for (var i = 1; i <= 5; i++) {
	steps.push(document.getElementById(`step${i}`));
}
var currentStepIndex = 0;

const getStatusIndex = (i) => {
	return i < currentStepIndex
  	? 0
    : i === currentStepIndex
    	? 1
      : 2;
};

const updateUi = () => {
	steps.forEach((step, i) => {
  	const statusClass = statusClasses[getStatusIndex(i)];
    step.className = `step ${statusClass}`;
  });
};

const next = () => {
	if (currentStepIndex + 1 < steps.length) {
  	currentStepIndex++;
    updateUi();
  }
};

const back = () => {
	if (currentStepIndex > 0) {
  	currentStepIndex--;
    updateUi();
  }
};

document.getElementById('back').addEventListener('click', back);
document.getElementById('next').addEventListener('click', next);

updateUi();