<main>
      <section id="jump-both">
        <div class="stepper translate" style="--easing: steps(var(--step-count), jump-both)"></div>
        <div class="stepper marker translate start"></div>
        <div class="stepper marker translate end"></div>
        <p>
          steps(<span class="count-label">4</span>, jump-both)
        </p>
      </section>
      <section>
        <div class="stepper translate" style="--easing: linear"></div>
        <div class="stepper marker translate start"></div>
        <div class="stepper marker translate end"></div>
        <p>
          linear
        </p>
      </section>
    </main>
    <aside></aside>
    <form>
      <p>
        <input id="step-count" type="number" step="1" value="4" min="1" max="20" />
        <label for="step-count">Step Count</label>
      </p>
      <p>
        <input type="number" value="4000" id="duration" />
        <label for="duration">Duration (ms)</label>
      </p>
      <p>
        <select id="direction">
          <option value="normal">normal</option> 
          <option value="alternate">alternate</option> 
          <option value="reverse">reverse</option> 
          <option value="alternate-reverse">alternate-reverse</option> 
        </select>
        <label for="direction">Direction</label>
      </p>
    </form>
    <footer>This browser does not yet support the newest <code>steps()</code> options: <code>jump-start</code>, <code>jump-end</code>, <code>jump-both</code>, and <code>jump-none</code>. To see the new easing options in action, try Firefox 65+.</footer>
:root {
  --duration: 4000ms;
  --step-count: 4;
}
.stepper {
  background: hsl(343, 100%, 54%);
  animation: none var(--duration) infinite var(--direction, normal) var(--easing, steps(var(--step-count), jump-start));
}
.translate {
  width: 2rem;
  height: 2rem;
  animation-name: move-right;
}

section {
  height: 6rem;
  position: relative;
}

@keyframes move-right {
  0% {
    transform: translateX(-10rem);
  }
  100% {
    transform: translateX(10rem);
  }
}


.marker {
  opacity: .6;
  animation: none;
  position: absolute;
  top: 0;
  background: none;
  border: 2px dashed hsla(343, 100%, 94%, .6);
}
.marker.start.translate {
  transform: translateX(-10rem);
}
.marker.end.translate {
  transform: translateX(10rem);
}


body {
  min-height: 100vh;
  padding: 6rem 1rem 3rem;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  align-items: center;
  overflow-x: hidden;
  font-family: var(--fonts);
  --fonts: system-ui, -apple-system, 'Segoe UI', sans-serif;
  color: #51515c;
  color: #f1f1fc;
  background: radial-gradient(circle, hsl(343, 50%, 5%) 70%, hsl(343, 50%, 2%))
}
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}


aside {
  height: 1.2rem;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  border-bottom: 3px double hsla(0,0%,70%,.5);
  background: rgba(0,0,0,.76);
}
aside::after {
  content: '';
  position: absolute;
  top: .1rem;
  right: 0;
  bottom: .2rem;
  left: 0;
  background: hsl(163, 100%, 40%);
  animation: progress var(--duration) 0ms infinite linear;
  opacity: .6;
  transform: scaleX(0);
  transform-origin: 0% 50%;
}

@keyframes progress {
  100% {
    transform: scaleX(1);
  }
}
form {
  display: grid;
  grid-gap: 2em 1em;
  gap: 2em 1em;
  grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
  width: 100%;
  max-width: 35rem;
}
form p {
  display: flex;
  flex-direction: column-reverse;
}
form label {
  margin-bottom: .5em;
}
select, input {
	-moz-appearance: none;
	-webkit-appearance: none;
	appearance: none;
  
	display: block;
  width: 100%;
	font-size: 16px;
	font-weight: 500;
  color: #444;
	line-height: 1.3;
	padding: .6em .4em .5em .4em;
	border: 1px solid #ccd;
	box-shadow: 0 1px 0 1px rgba(0,0,0,.04);
	border-radius: .25em;
	background-image: var(--top-background, none),
	  linear-gradient(to bottom, rgba(255,255,255,.8) 0%, rgba(245,245,255,.8) 100%);
	background-repeat: no-repeat, repeat;
	background-position: right .7em top 50%, 0 0;
	background-size: .65em auto, 100%;
}
[disabled],
[disabled] + label {
  opacity: .6;
}
select {
  font-family: var(--fonts);
  padding-right: 1.6em;
  --top-background: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23667CB2%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E');
}
select::-ms-expand {
	display: none;
}
select:hover,
input:hover {
	border-color: #778;
}
select:focus,
input:focus {
	border-color: #ccd;
	box-shadow: 0 0 1px 3px hsla(163, 100%, 40%, .6);
	color: #222; 
	outline: none;
}
select option {
	font-weight: normal;
}



footer {
  line-height: 1.4;
  width: 100%;
  max-width: 64rem;
}
footer code {
  font-size: 1.2em;
}

#jump-none,
#jump-both,
#jump-end,
#jump-start {
  display: none;
}

@supports (animation-timing-function: steps(2, jump-both)) {
  #jump-both {
    display: flex;
  }
  footer {
    display: none;
  }
}

@supports (animation-timing-function: steps(2, jump-none)) {
  #jump-none {
    display: flex;
  }
  footer {
    display: none;
  }
}

@supports (animation-timing-function: steps(2, jump-start)) {
  #jump-start {
    display: flex;
  }
  #start {
    display: none;
  }
}

@supports (animation-timing-function: steps(2, jump-end)) {
  #jump-end {
    display: flex;
  }
  #end {
    display: none;
  }
}



.side-by-side main {
  display: grid;
  
  grid-template-columns: repeat(auto-fit, minmax(26rem, 1fr));
  grid-auto-rows: 6rem;
  grid-row-gap: 2rem;
  row-gap: 2rem;
  width: 100%;
  max-width: 58rem;
  place-items: center;
}
.side-by-side section {
  display: flex;
  flex-direction: column;
  align-items: center;
}
.side-by-side .stepper {
  margin-bottom: 1rem;
}

const options = document.querySelectorAll('input, select');
const progress = document.querySelector('aside');
const stepper = document.querySelector('.stepper');
const docStyle = document.documentElement.style;

const stepCount = document.getElementById('step-count');
const duration = document.getElementById('duration');
const direction = document.getElementById('direction');

setGuides(parseInt(stepCount.value, 10));

options.forEach(el => {
  el.addEventListener('change', e => {
    let value = e.currentTarget.value;
    if (e.currentTarget.id === 'duration') {
      value = value + 'ms';
    }
    if (e.currentTarget.id === 'step-count') {
      setGuides(value);
      if (document.documentElement.classList.contains('side-by-side')) {
        updateLabels(value); 
      }
    }
    if (e.currentTarget.id === 'easing') {
      if (value.includes('steps')) {
        stepCount.removeAttribute('disabled')
      } else {
        stepCount.setAttribute('disabled', 'disabled')
      }
    }
    docStyle.setProperty(`--${e.currentTarget.id}`, value);
  });
});

function setGuides(count) {
  let images = [];
  for (let i = 0; i < count; ++i) {
    images.push(`linear-gradient(
      to right,
      hsla(0,0%,70%,0) ${i / count * 100}%,
      hsla(0,0%,70%,.5) ${i / count * 100}%,
      hsla(0,0%,70%,.5) calc(${i / count * 100}% + 2px),
      hsla(0,0%,70%,0) calc(${i / count * 100}% + 2px))`)
  }
  progress.style.backgroundImage = images.join(',');
}

const countLabels = document.querySelectorAll('.count-label');
function updateLabels(value) {
   countLabels.forEach(label => {
     label.textContent = value;
   });
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.