<main>
<section id="jump-start">
<div class="stepper translate" style="--easing: steps(var(--step-count), jump-start)"></div>
<div class="stepper marker translate start"></div>
<div class="stepper marker translate end"></div>
<p>
steps(<span class="count-label">5</span>, jump-start)
</p>
</section>
<section id="jump-end">
<div class="stepper translate" style="--easing: steps(var(--step-count), jump-end)"></div>
<div class="stepper marker translate start"></div>
<div class="stepper marker translate end"></div>
<p>
steps(<span class="count-label">5</span>, jump-end)
</p>
</section>
<section id="jump-none">
<div class="stepper translate" style="--easing: steps(var(--step-count), jump-none)"></div>
<div class="stepper marker translate start"></div>
<div class="stepper marker translate end"></div>
<p>
steps(<span class="count-label">5</span>, jump-none)
</p>
</section>
<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">5</span>, jump-both)
</p>
</section>
<section id="start">
<div class="stepper translate" style="--easing: steps(var(--step-count), start)"></div>
<div class="stepper marker translate start"></div>
<div class="stepper marker translate end"></div>
<p>
steps(<span class="count-label">5</span>, start)
</p>
</section>
<section id="end">
<div class="stepper translate" style="--easing: steps(var(--step-count), end)"></div>
<div class="stepper marker translate start"></div>
<div class="stepper marker translate end"></div>
<p>
steps(<span class="count-label">5</span>, end)
</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>
<section>
<div class="stepper translate" style="--easing: ease"></div>
<div class="stepper marker translate start"></div>
<div class="stepper marker translate end"></div>
<p>
ease
</p>
</section>
<section>
<div class="stepper translate" style="--easing: ease-in-out"></div>
<div class="stepper marker translate start"></div>
<div class="stepper marker translate end"></div>
<p>
ease-in-out
</p>
</section>
<section>
<div class="stepper translate" style="--easing: cubic-bezier(.25,-0.25,.75,1.25)"></div>
<div class="stepper marker translate start"></div>
<div class="stepper marker translate end"></div>
<p>
cubic-bezier(.25,-0.25,.75,1.25)
</p>
</section>
</main>
<aside></aside>
<form>
<p>
<input id="step-count" type="number" step="1" value="5" 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" selected>alternate</option>
<option value="reverse">reverse</option>
<option value="alternate-reverse">alternate-reverse</option>
</select>
<label for="direction">Direction</label>
</p>
</form>
<p id="rule"></p>
<footer>This browser does not yet support the <a href="https://danielcwilson.com/blog/2019/02/step-and-jump" target="_blank">newest <code>steps()</code> options</a>: <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: 5;
}
.stepper {
background: hsl(343, 100%, 54%);
animation: none var(--duration) infinite var(--direction, alternate) var(--easing, steps(var(--step-count), jump-start));
}
.rotate {
width: .5rem;
height: 2rem;
animation-name: spin;
//border-radius: .25rem;
}
.translate {
width: 2rem;
height: 2rem;
animation-name: move-right;
}
.hue-rotate {
width: 10rem;
height: 2rem;
animation-name: color;
}
section {
height: 4rem;
position: relative;
font-family: monospace;
}
@keyframes spin {
0% {
transform: rotate(0deg) translateY(-2rem);
}
100% {
transform: rotate(360deg) translateY(-2rem);
}
}
@keyframes move-right {
0% {
transform: translateX(-6rem);
}
100% {
transform: translateX(6rem);
}
}
@keyframes color {
0% {
filter: hue-rotate(0deg);
}
100% {
filter: hue-rotate(360deg);
}
}
.marker {
opacity: .6;
animation: none;
position: absolute;
top: 0;
background: none;
border: 2px dashed hsla(343, 100%, 94%, .6);
}
.marker.start.translate {
transform: translateX(-6rem);
}
.marker.end.translate {
transform: translateX(6rem);
}
.marker.start.rotate {
transform: rotate(0deg) translateY(-2rem);
}
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;
}
#rule {
margin: 3rem;
font-family: monospace;
font-size: 1.6em;
line-height: 1.4;
width: 100%;
max-width: 64rem;
word-break: keep-all;
text-indent: -2rem;
padding-left: 2rem;
text-align: center;
}
footer {
line-height: 1.4;
width: 100%;
max-width: 64rem;
}
footer code {
font-family: monospace;
font-size: 1.2em;
}
a {
color: white;
}
#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(16rem, 1fr));
grid-auto-rows: 4rem;
grid-row-gap: 2rem;
row-gap: 2rem;
width: 100%;
max-width: 58rem;
place-items: center;
margin-bottom: 4rem;
}
.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 rule = document.getElementById('rule');
const stepCount = document.getElementById('step-count');
const duration = document.getElementById('duration');
const direction = document.getElementById('direction');
setGuides(parseInt(stepCount.value, 10));
updateRule(true);
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);
updateRule();
});
});
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(',');
}
function updateRule(initial) {
const s = window.getComputedStyle(stepper);
rule.textContent = `animation: go ${s.animationDuration} infinite ${s.animationDirection}`;
if (initial && s.animationTimingFunction === 'ease') {
document.getElementById('start').setAttribute('selected','selected');
docStyle.setProperty('--easing', document.getElementById('start').value);
}
}
const countLabels = document.querySelectorAll('.count-label');
function updateLabels(value) {
countLabels.forEach(label => {
label.textContent = value;
});
}
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.