A gift card to my wife
A Pen by radfox200289 on CodePen.
| <audio id="music" loop> | |
| <source src="https://cdn.pixabay.com/download/audio/2022/03/15/audio_4c7c3c6b8b.mp3" type="audio/mpeg"> | |
| </audio> | |
| <div class="start" onclick="startExperience()"> | |
| <div class="bunny"> | |
| 🐰 | |
| <span class="heart">❤️</span> | |
| </div> | |
| <p>Tap the bunny</p> | |
| </div> | |
| <div class="sky hidden" id="sky"> | |
| <div class="star you">★</div> | |
| <div class="star her">★</div> | |
| <div class="card"> | |
| <h1>Happy New Year</h1> | |
| <h2>Vanessa</h2> | |
| <p id="text"></p> | |
| <p class="signature">Love, Leila ❤️</p> | |
| </div> | |
| </div> | |
| <canvas id="fireworks"></canvas> |
| const music = document.getElementById("music"); | |
| const sky = document.getElementById("sky"); | |
| const textEl = document.getElementById("text"); | |
| const message = ` | |
| To my beautiful wife, | |
| May every New Year’s Eve hold us together. | |
| You are the best thing that happened to me. | |
| May 2026 be the year that brings us together. | |
| Until the day we meet — | |
| shine, my love. | |
| Dance. Eat. Feel joy. | |
| Be everything you want to be. | |
| Happy New Year 💫 | |
| `; | |
| let i = 0; | |
| function typeWriter() { | |
| if (i < message.length) { | |
| textEl.innerHTML += message.charAt(i); | |
| i++; | |
| setTimeout(typeWriter, 45); | |
| } | |
| } | |
| function startExperience() { | |
| document.querySelector(".start").style.display = "none"; | |
| sky.classList.remove("hidden"); | |
| setTimeout(() => sky.classList.add("active"), 500); | |
| music.volume = 0.4; | |
| music.play(); | |
| setTimeout(typeWriter, 2000); | |
| startFireworks(); | |
| } | |
| /* Fireworks */ | |
| const canvas = document.getElementById("fireworks"); | |
| const ctx = canvas.getContext("2d"); | |
| canvas.width = window.innerWidth; | |
| canvas.height = window.innerHeight; | |
| function random(min, max) { | |
| return Math.random() * (max - min) + min; | |
| } | |
| function startFireworks() { | |
| setInterval(() => { | |
| const x = random(100, canvas.width - 100); | |
| const y = random(50, canvas.height / 2); | |
| for (let i = 0; i < 40; i++) { | |
| fireworks.push({ | |
| x, y, | |
| dx: random(-3, 3), | |
| dy: random(-3, 3), | |
| life: 60 | |
| }); | |
| } | |
| }, 1200); | |
| } | |
| let fireworks = []; | |
| function animate() { | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| fireworks.forEach((p, index) => { | |
| p.x += p.dx; | |
| p.y += p.dy; | |
| p.life--; | |
| ctx.fillStyle = "rgba(255,182,193,0.8)"; | |
| ctx.fillRect(p.x, p.y, 2, 2); | |
| if (p.life <= 0) fireworks.splice(index, 1); | |
| }); | |
| requestAnimationFrame(animate); | |
| } | |
| animate(); |
| * { | |
| box-sizing: border-box; | |
| font-family: 'Georgia', serif; | |
| } | |
| body { | |
| margin: 0; | |
| height: 100vh; | |
| background: black; | |
| overflow: hidden; | |
| color: white; | |
| } | |
| .start { | |
| position: fixed; | |
| inset: 0; | |
| display: flex; | |
| flex-direction: column; | |
| justify-content: center; | |
| align-items: center; | |
| background: black; | |
| cursor: pointer; | |
| } | |
| .gift { | |
| font-size: 90px; | |
| animation: pulse 2s infinite; | |
| } | |
| @keyframes pulse { | |
| 0% { transform: scale(1); } | |
| 50% { transform: scale(1.15); } | |
| 100% { transform: scale(1); } | |
| } | |
| .sky { | |
| position: fixed; | |
| inset: 0; | |
| background: radial-gradient(circle at top, #1b0036, #000); | |
| } | |
| .hidden { | |
| display: none; | |
| } | |
| /* Stars */ | |
| .star { | |
| position: absolute; | |
| top: 30%; | |
| font-size: 30px; | |
| color: #ffd1dc; | |
| transition: all 12s ease; | |
| } | |
| .you { | |
| left: 10%; | |
| } | |
| .her { | |
| right: 10%; | |
| } | |
| .sky.active .you { | |
| left: 45%; | |
| } | |
| .sky.active .her { | |
| right: 45%; | |
| } | |
| /* Message card */ | |
| .card { | |
| position: absolute; | |
| bottom: 10%; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| width: 340px; | |
| padding: 30px; | |
| border-radius: 25px; | |
| background: rgba(255,255,255,0.08); | |
| backdrop-filter: blur(12px); | |
| text-align: center; | |
| box-shadow: 0 0 40px rgba(255,182,193,0.25); | |
| } | |
| h1 { | |
| color: #ffd1dc; | |
| margin-bottom: 5px; | |
| } | |
| h2 { | |
| color: #ff9eb5; | |
| letter-spacing: 2px; | |
| } | |
| .signature { | |
| margin-top: 20px; | |
| color: #ffd1dc; | |
| font-size: 1.1em; | |
| } | |
| canvas { | |
| position: fixed; | |
| inset: 0; | |
| pointer-events: none; | |
| }.bunny { | |
| font-size: 90px; | |
| position: relative; | |
| animation: hop 1.8s infinite ease-in-out; | |
| } | |
| .bunny .heart { | |
| position: absolute; | |
| top: -10px; | |
| right: -10px; | |
| font-size: 24px; | |
| animation: floatHeart 2s infinite ease-in-out; | |
| } | |
| @keyframes hop { | |
| 0%, 100% { | |
| transform: translateY(0); | |
| } | |
| 50% { | |
| transform: translateY(-18px); | |
| } | |
| } | |
| @keyframes floatHeart { | |
| 0% { | |
| opacity: 0; | |
| transform: translateY(0) scale(0.8); | |
| } | |
| 50% { | |
| opacity: 1; | |
| transform: translateY(-10px) scale(1); | |
| } | |
| 100% { | |
| opacity: 0; | |
| transform: translateY(-20px) scale(1.2); | |
| } | |
| } |
A gift card to my wife
A Pen by radfox200289 on CodePen.