Curtain wipe
A coloured panel sweeps up to cover the screen, the view underneath swaps, then the panel sweeps away.
CSSJavaScriptIntermediate
<div class="view" id="view" data-v="a"><div><h1 id="h">Page one</h1><button id="go">Go to the other page</button></div></div>
<div class="curtain" id="curtain"></div>
body{overflow:hidden;display:block}
.view{position:absolute;inset:0;display:grid;place-items:center;text-align:center;padding:20px}
.view h1{margin:0 0 18px;font-size:clamp(2.2rem,9vw,4rem);letter-spacing:-.03em}
.view[data-v="a"]{background:#140807}
.view[data-v="b"]{background:#1a0f0e}
button{font:inherit;font-weight:700;background:#f08a5d;color:#270100;border:0;border-radius:10px;padding:12px 22px;cursor:pointer}
button:active{transform:scale(.96)}
.curtain{position:fixed;inset:0;background:#f08a5d;transform:translateY(100%);z-index:10;pointer-events:none}
.curtain.cover{animation:cover .6s cubic-bezier(.77,0,.18,1) forwards}
.curtain.uncover{transform:translateY(0);animation:uncover .6s cubic-bezier(.77,0,.18,1) forwards}
@keyframes cover{from{transform:translateY(100%)}to{transform:translateY(0)}}
@keyframes uncover{from{transform:translateY(0)}to{transform:translateY(-100%)}}
const view = document.getElementById('view');
const h = document.getElementById('h');
const curtain = document.getElementById('curtain');
const go = document.getElementById('go');
let busy = false;
go.onclick = () => {
if (busy) return; busy = true;
curtain.className = 'curtain cover'; // 1. cover the screen
curtain.addEventListener('animationend', () => {
const toB = view.dataset.v === 'a'; // 2. swap while hidden
view.dataset.v = toB ? 'b' : 'a';
h.textContent = toB ? 'Page two' : 'Page one';
curtain.className = 'curtain uncover'; // 3. reveal the new page
curtain.addEventListener('animationend', () => {
curtain.className = 'curtain'; busy = false;
}, { once: true });
}, { once: true });
};
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Curtain wipe</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;padding:20px}
.view h1{margin:0 0 18px;font-size:clamp(2.2rem,9vw,4rem);letter-spacing:-.03em}
.view[data-v="a"]{background:#140807}
.view[data-v="b"]{background:#1a0f0e}
button{font:inherit;font-weight:700;background:#f08a5d;color:#270100;border:0;border-radius:10px;padding:12px 22px;cursor:pointer}
button:active{transform:scale(.96)}
.curtain{position:fixed;inset:0;background:#f08a5d;transform:translateY(100%);z-index:10;pointer-events:none}
.curtain.cover{animation:cover .6s cubic-bezier(.77,0,.18,1) forwards}
.curtain.uncover{transform:translateY(0);animation:uncover .6s cubic-bezier(.77,0,.18,1) forwards}
@keyframes cover{from{transform:translateY(100%)}to{transform:translateY(0)}}
@keyframes uncover{from{transform:translateY(0)}to{transform:translateY(-100%)}}
</style></head>
<body>
<div class="view" id="view" data-v="a"><div><h1 id="h">Page one</h1><button id="go">Go to the other page</button></div></div>
<div class="curtain" id="curtain"></div>
<script>
const view = document.getElementById('view');
const h = document.getElementById('h');
const curtain = document.getElementById('curtain');
const go = document.getElementById('go');
let busy = false;
go.onclick = () => {
if (busy) return; busy = true;
curtain.className = 'curtain cover'; // 1. cover the screen
curtain.addEventListener('animationend', () => {
const toB = view.dataset.v === 'a'; // 2. swap while hidden
view.dataset.v = toB ? 'b' : 'a';
h.textContent = toB ? 'Page two' : 'Page one';
curtain.className = 'curtain uncover'; // 3. reveal the new page
curtain.addEventListener('animationend', () => {
curtain.className = 'curtain'; busy = false;
}, { once: true });
}, { once: true });
};
</script>
</body></html>
Good for
Navigating between pages or views in a single-page site, or as a cover while a multi-page site loads.
Watch out for
Swap the content only while the curtain fully covers the screen. For real multi-page sites, play the exit, navigate, then play the entrance on the next page.
Accessibility
Wrap motion in @media (prefers-reduced-motion: reduce) and switch it off or shorten it for people who ask for less movement.