Slide push
The current view slides out one side while the next slides in, like a native app navigation.
CSSJavaScriptIntermediate
<div class="view v1" id="v0"><div><h1>Step 1</h1><div class="nav"><button data-go="1">Next</button></div></div></div>
<div class="view v2 right" id="v1"><div><h1>Step 2</h1><div class="nav"><button class="ghost" data-go="0">Back</button><button data-go="2">Next</button></div></div></div>
<div class="view v3 right" id="v2"><div><h1>Step 3</h1><div class="nav"><button class="ghost" data-go="1">Back</button></div></div></div>
body{overflow:hidden;display:block}
.view{position:absolute;inset:0;display:grid;place-items:center;text-align:center;transition:transform .55s cubic-bezier(.2,.8,.2,1);padding:20px}
.view h1{margin:0 0 16px;font-size:clamp(2rem,8vw,3.4rem);letter-spacing:-.03em}
.v1{background:#140807}.v2{background:#2a0f0b}.v3{background:#1f1210}
.view.right{transform:translateX(100%)}
.view.left{transform:translateX(-30%)}
.nav{display:flex;gap:10px;justify-content:center}
button{font:inherit;font-weight:700;background:#f08a5d;color:#270100;border:0;border-radius:10px;padding:10px 18px;cursor:pointer}
button.ghost{background:#3a2220;color:#fff}
button:active{transform:scale(.96)}
const views = [...document.querySelectorAll('.view')];
let current = 0;
function show(next) {
views.forEach((v, i) => {
v.classList.toggle('left', i < next); // earlier views rest on the left
v.classList.toggle('right', i > next); // later views wait on the right
});
current = next;
}
document.querySelectorAll('[data-go]').forEach(b => b.onclick = () => show(+b.dataset.go));
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Slide push</title>
<style>
*{box-sizing:border-box}
html,body{margin:0;height:100%}
body{display:grid;place-items:center;background:#140807;color:#fffffc;font:16px/1.5 system-ui,-apple-system,"Segoe UI",sans-serif}
body{overflow:hidden;display:block;place-items:normal}
.view{position:absolute;inset:0;display:grid;place-items:center;text-align:center;transition:transform .55s cubic-bezier(.2,.8,.2,1);padding:20px}
.view h1{margin:0 0 16px;font-size:clamp(2rem,8vw,3.4rem);letter-spacing:-.03em}
.v1{background:#140807}.v2{background:#2a0f0b}.v3{background:#1f1210}
.view.right{transform:translateX(100%)}
.view.left{transform:translateX(-30%)}
.nav{display:flex;gap:10px;justify-content:center}
button{font:inherit;font-weight:700;background:#f08a5d;color:#270100;border:0;border-radius:10px;padding:10px 18px;cursor:pointer}
button.ghost{background:#3a2220;color:#fff}
button:active{transform:scale(.96)}
</style></head>
<body>
<div class="view v1" id="v0"><div><h1>Step 1</h1><div class="nav"><button data-go="1">Next</button></div></div></div>
<div class="view v2 right" id="v1"><div><h1>Step 2</h1><div class="nav"><button class="ghost" data-go="0">Back</button><button data-go="2">Next</button></div></div></div>
<div class="view v3 right" id="v2"><div><h1>Step 3</h1><div class="nav"><button class="ghost" data-go="1">Back</button></div></div></div>
<script>
const views = [...document.querySelectorAll('.view')];
let current = 0;
function show(next) {
views.forEach((v, i) => {
v.classList.toggle('left', i < next); // earlier views rest on the left
v.classList.toggle('right', i > next); // later views wait on the right
});
current = next;
}
document.querySelectorAll('[data-go]').forEach(b => b.onclick = () => show(+b.dataset.go));
</script>
</body></html>
Good for
Step-by-step flows, onboarding, wizards and mobile style navigation.
Watch out for
Keep both views mounted during the move, then clean up. Reverse the direction when going back.
Accessibility
Wrap motion in @media (prefers-reduced-motion: reduce) and switch it off or shorten it for people who ask for less movement.