-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Open
Description
<title>Will you be my Valentine?</title>
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.3/dist/confetti.browser.min.js"></script>
<style>
:root {
--bg1: #ffd6e7;
--bg2: #ffeef6;
--card: #ffffffcc;
--yes: #ff3b7a;
--yesHover: #ff1f68;
}
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100svh;
display: grid;
place-items: center;
background: radial-gradient(circle at top, var(--bg2), var(--bg1));
font-family: system-ui, sans-serif;
overflow: hidden;
padding: 16px;
}
/* FULL-SCREEN CONFETTI CANVAS */
#confettiCanvas {
position: fixed;
inset: 0;
width: 100vw;
height: 100vh;
pointer-events: none;
z-index: 9999;
}
.card {
width: min(720px, 92vw);
padding: 26px 22px;
background: var(--card);
backdrop-filter: blur(10px);
border-radius: 22px;
text-align: center;
box-shadow: 0 18px 60px rgba(0,0,0,.15);
}
.art {
width: min(260px, 80vw);
margin: 0 auto 10px;
display: block;
filter: drop-shadow(0 10px 14px rgba(0,0,0,.12));
}
h1 {
font-size: clamp(26px, 4vw, 44px);
margin: 12px 0 18px;
}
.button-zone {
position: relative;
width: min(520px, 92%);
height: 150px;
margin: 0 auto;
touch-action: none;
}
button {
position: absolute;
top: 50%;
transform: translateY(-50%);
padding: 16px 24px;
font-size: 18px;
font-weight: 800;
border-radius: 999px;
border: none;
cursor: pointer;
box-shadow: 0 10px 24px rgba(0,0,0,.14);
user-select: none;
-webkit-tap-highlight-color: transparent;
transition: transform .12s ease, background .12s ease;
}
#yesBtn {
left: 18%;
background: var(--yes);
color: #fff;
}
#yesBtn:hover { background: var(--yesHover); }
#noBtn {
left: 62%;
background: #e5e7eb;
color: #111827;
}
.hint {
margin-top: 10px;
font-size: 13px;
opacity: .7;
}
.result {
display: none;
margin-top: 18px;
animation: pop .35s ease;
}
.result h2 {
font-size: clamp(30px, 4.5vw, 46px);
margin: 10px 0;
}
.fireworks {
width: min(380px, 90vw);
margin: 0 auto;
display: block;
}
@Keyframes pop {
from { transform: scale(.96); opacity: 0; }
to { transform: scale(1); opacity: 1; }
}
</style>
<script>
const zone = document.getElementById("zone");
const yesBtn = document.getElementById("yesBtn");
const noBtn = document.getElementById("noBtn");
const result = document.getElementById("result");
const hint = document.getElementById("hint");
/* ---------- CONFETTI ---------- */
const confettiCanvas = document.getElementById("confettiCanvas");
function resizeConfettiCanvas() {
const dpr = Math.max(1, window.devicePixelRatio || 1);
confettiCanvas.width = Math.floor(window.innerWidth * dpr);
confettiCanvas.height = Math.floor(window.innerHeight * dpr);
confettiCanvas.style.width = "100vw";
confettiCanvas.style.height = "100vh";
}
resizeConfettiCanvas();
window.addEventListener("resize", resizeConfettiCanvas);
window.addEventListener("orientationchange", () => setTimeout(resizeConfettiCanvas, 150));
const confettiInstance = confetti.create(confettiCanvas, {
resize: false,
useWorker: true
});
function fullScreenConfetti() {
const end = Date.now() + 1600;
(function frame() {
confettiInstance({
particleCount: 12,
spread: 90,
startVelocity: 45,
ticks: 180,
origin: { x: Math.random(), y: Math.random() * 0.3 }
});
if (Date.now() < end) requestAnimationFrame(frame);
})();
setTimeout(() => {
confettiInstance({
particleCount: 300,
spread: 140,
startVelocity: 60,
ticks: 220,
origin: { x: 0.5, y: 0.55 }
});
}, 300);
}
/* ---------- YES BUTTON GROWS ---------- */
let yesScale = 1;
function growYes() {
yesScale = Math.min(2.2, yesScale + 0.1);
yesBtn.style.transform = `translateY(-50%) scale(${yesScale})`;
}
/* ---------- NO BUTTON RUNS AWAY ---------- */
function clamp(n, min, max) {
return Math.max(min, Math.min(max, n));
}
function moveNo(px, py) {
const z = zone.getBoundingClientRect();
const b = noBtn.getBoundingClientRect();
let dx = (b.left + b.width / 2) - px;
let dy = (b.top + b.height / 2) - py;
let mag = Math.hypot(dx, dy) || 1;
dx /= mag;
dy /= mag;
let newLeft = (b.left - z.left) + dx * 150;
let newTop = (b.top - z.top) + dy * 150;
newLeft = clamp(newLeft, 0, z.width - b.width);
newTop = clamp(newTop, 0, z.height - b.height);
noBtn.style.left = newLeft + "px";
noBtn.style.top = newTop + "px";
noBtn.style.transform = "none";
growYes();
}
zone.addEventListener("pointermove", e => {
const b = noBtn.getBoundingClientRect();
const d = Math.hypot(
(b.left + b.width / 2) - e.clientX,
(b.top + b.height / 2) - e.clientY
);
if (d < 140) moveNo(e.clientX, e.clientY);
});
noBtn.addEventListener("click", e => e.preventDefault());
/* ---------- YES CLICK ---------- */
yesBtn.addEventListener("click", () => {
zone.style.display = "none";
hint.style.display = "none"; // HIDE THE HINT
result.style.display = "block";
resizeConfettiCanvas();
fullScreenConfetti();
});
</script>
<path d="M250 50 C250 33 270 25 282 38
C294 25 314 33 314 50
C314 78 282 92 282 106
C282 92 250 78 250 50Z"
fill="url(#heart)"/>
<path d="M90 120 C90 70 140 40 190 60
C240 40 290 70 290 120
C290 180 240 210 190 210
C140 210 90 180 90 120Z"
fill="url(#fur)"/>
<path d="M110 92 L95 55 L140 78 Z" fill="#f2a97b"/>
<path d="M270 92 L285 55 L240 78 Z" fill="#f2a97b"/>
<circle cx="160" cy="130" r="8"/>
<circle cx="220" cy="130" r="8"/>
<path d="M190 144 C186 144 182 148 182 152
C182 160 190 164 190 170
C190 164 198 160 198 152
C198 148 194 144 190 144Z"
fill="#ff7aa2"/>
</svg>
<h1>nirali will you be my valentine?</h1>
<section class="button-zone" id="zone">
<button id="yesBtn">Yes</button>
<button id="noBtn">No</button>
</section>
<!-- HINT -->
<div class="hint" id="hint">“No” seems a bit shy 😈</div>
<section class="result" id="result">
<h2>YAY! 🎉</h2>
<img
class="fireworks"
src="https://media.giphy.com/media/26ufdipQqU2lhNA4g/giphy.gif"
alt="Fireworks"
/>
</section>
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels