:root {
color-scheme: light;
--bg: #f4f2ed;
--ink: #1f1b17;
--muted: #72665b;
--accent: #8a6b5a;
--radius: 26px;
--shadow: 0 34px 90px rgba(31, 27, 23, 0.2);
font-family: "Manrope", sans-serif;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
min-height: 100vh;
background: radial-gradient(circle at top, #fff7ef 0%, #f2ece3 45%, #e4dbcf 100%);
color: var(--ink);
display: flex;
align-items: center;
justify-content: center;
padding: 32px 16px;
}
.wrap {
width: min(980px, 100%);
display: grid;
gap: 20px;
}
header {
display: grid;
gap: 8px;
}
.eyebrow {
text-transform: uppercase;
letter-spacing: 0.3em;
font-size: 0.7rem;
color: var(--muted);
}
h1 {
font-family: "Prata", serif;
font-size: clamp(2.4rem, 4.6vw, 4rem);
}
.sub {
color: var(--muted);
}
.stage {
position: relative;
height: clamp(350px, 52vh, 500px);
}
.card {
position: absolute;
inset: 0;
border-radius: var(--radius);
overflow: hidden;
box-shadow: var(--shadow);
opacity: 0;
pointer-events: none;
}
.card.is-active {
opacity: 1;
pointer-events: auto;
}
.wipe {
position: absolute;
inset: 0;
background-size: cover;
background-position: center;
clip-path: polygon(0 0, 0 0, 0 100%, 0 100%);
}
.card.is-active .wipe {
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
}
.label {
position: absolute;
left: 24px;
bottom: 24px;
color: #fff;
text-shadow: 0 18px 30px rgba(0, 0, 0, 0.35);
}
.label span {
font-size: 0.7rem;
letter-spacing: 0.3em;
}
.label h2 {
font-family: "Prata", serif;
font-size: clamp(1.5rem, 3vw, 2.4rem);
}
.meter {
height: 4px;
background: rgba(31, 27, 23, 0.15);
border-radius: 999px;
overflow: hidden;
}
.meter .fill {
height: 100%;
width: 0%;
background: linear-gradient(90deg, var(--accent), #c6a997);
}
const cards = Array.from(document.querySelectorAll(".card"));
const fill = document.querySelector(".meter .fill");
const intervalMs = 4100;
let active = 0;
function setupWipes() {
cards.forEach((card) => {
const wipe = card.querySelector(".wipe");
wipe.style.backgroundImage = `url('${wipe.dataset.image}')`;
});
}
function setActive(index) {
cards.forEach((card, i) => {
card.classList.toggle("is-active", i === index);
card.style.zIndex = `${cards.length - i}`;
});
}
function transition() {
const current = cards[active];
active = (active + 1) % cards.length;
const next = cards[active];
setActive(active);
const currentWipe = current.querySelector(".wipe");
const nextWipe = next.querySelector(".wipe");
const tl = gsap.timeline({ defaults: { duration: 0.8, ease: "power2.inOut" } });
tl.set(nextWipe, {
clipPath: "polygon(0 0, 0 0, 0 100%, 0 100%)",
})
.to(currentWipe, {
clipPath: "polygon(100% 0, 100% 0, 100% 100%, 100% 100%)",
})
.to(
nextWipe,
{
clipPath: "polygon(0 0, 100% 0, 100% 100%, 0 100%)",
},
"<"
);
}
function animateMeter() {
gsap.fromTo(fill, { width: "0%" }, { width: "100%", duration: intervalMs / 1000, ease: "none" });
}
setupWipes();
setActive(active);
animateMeter();
setInterval(() => {
transition();
animateMeter();
}, intervalMs);
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Diagonal Cards</title>
<link
href="https://fonts.googleapis.com/css2?family=Manrope:wght@300;400;600&family=Prata&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main class="wrap">
<header>
<p class="eyebrow">Diagonal effect</p>
<h1>Diagonal Wipe Cards</h1>
<p class="sub">Images wipe across a diagonal mask.</p>
</header>
<section class="stage" aria-live="polite">
<article class="card is-active" data-index="0">
<div class="wipe" data-image="https://images.unsplash.com/photo-1469474968028-56623f02e42e?auto=format&fit=crop&w=1400&q=80"></div>
<div class="label"><span>01</span><h2>Desert Diagonal</h2></div>
</article>
<article class="card" data-index="1">
<div class="wipe" data-image="https://images.unsplash.com/photo-1500530855697-b586d89ba3ee?auto=format&fit=crop&w=1400&q=80"></div>
<div class="label"><span>02</span><h2>Fog Diagonal</h2></div>
</article>
<article class="card" data-index="2">
<div class="wipe" data-image="https://images.unsplash.com/photo-1507525428034-b723cf961d3e?auto=format&fit=crop&w=1400&q=80"></div>
<div class="label"><span>03</span><h2>Wave Diagonal</h2></div>
</article>
</section>
<div class="meter"><div class="fill"></div></div>
</main>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
<script src="script.js"></script>
</body>
</html>