<section class="demo">
<button type="button" class="demo__btn" id="btn">
<span class="demo__fill" id="fill"></span>
<span class="demo__label">Fill the room</span>
</button>
</section>.demo {
min-height: 100dvh;
display: grid;
place-items: center;
background: #08080a;
font-family: "Helvetica Neue", system-ui, sans-serif;
}
.demo__btn {
position: relative;
overflow: hidden;
border: 1px solid #2a2a32;
background: transparent;
color: #f4f4f2;
border-radius: 999px;
padding: 16px 28px;
font: 15px/1 "Helvetica Neue", system-ui, sans-serif;
letter-spacing: -0.01em;
cursor: pointer;
}
.demo__fill {
position: absolute;
inset: 0;
background: #f4f4f2;
clip-path: ellipse(80% 18% at 50% 118%);
pointer-events: none;
}
.demo__label {
position: relative;
z-index: 1;
mix-blend-mode: difference;
}const fill = document.querySelector("#fill");
const btn = document.querySelector("#btn");
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
gsap.set(fill, { clipPath: "ellipse(80% 22% at 50% 118%)" });
if (!reduce) {
gsap.to(fill, {
clipPath: "ellipse(80% 28% at 50% 112%)",
duration: 1.8,
ease: "sine.inOut",
yoyo: true,
repeat: -1,
});
}
btn.addEventListener("pointerenter", function () {
gsap.to(fill, {
clipPath: "ellipse(120% 120% at 50% 50%)",
duration: reduce ? 0 : 0.7,
ease: "power3.inOut",
overwrite: true,
});
});
btn.addEventListener("pointerleave", function () {
gsap.to(fill, {
clipPath: "ellipse(80% 22% at 50% 118%)",
duration: reduce ? 0 : 0.55,
ease: "power3.inOut",
overwrite: true,
});
});