CodeCraft Chronicles

My Wedding: A Handcrafted Digital Invitation

My Wedding: A wedding e-card built with HTML, CSS, and vanilla JavaScript — falling petals animation, responsive layout, smooth interactivity, no external libraries. One hundred percent handcrafted.

Why Build It Yourself

You could use a wedding invitation service. Upload a photo, pick a template, get a link. Done in twenty minutes.

Building it takes longer. But it's yours — every pixel, every animation parameter, every transition timing. When you share a link to something you built, there's a different feeling to it than sharing a link to something a platform generated for you.

For a wedding invitation, that feeling matters.

The Falling Petals

The animation is the visual centerpiece — white and pink petals falling from the top of the screen at slightly different speeds, sizes, and trajectories, with a gentle sway.

function createPetal() {
  const petal = document.createElement('div');
  petal.classList.add('petal');

  // Randomize each petal
  const size     = 8 + Math.random() * 16;    // 8–24px
  const startX   = Math.random() * 100;        // 0–100vw
  const duration = 4 + Math.random() * 6;      // 4–10s
  const delay    = Math.random() * 5;          // 0–5s stagger
  const sway     = (Math.random() - 0.5) * 60; // horizontal drift

  petal.style.cssText = `
    width:  ${size}px;
    height: ${size}px;
    left:   ${startX}vw;
    animation-duration:  ${duration}s;
    animation-delay:    -${delay}s;
    --sway: ${sway}px;
  `;

  document.querySelector('.petals-container').appendChild(petal);

  // Remove after animation completes
  petal.addEventListener('animationend', () => petal.remove());
}

// Continuously create petals
setInterval(createPetal, 300);

The CSS handles the falling and swaying via @keyframes:

@keyframes fall {
  0% {
    transform: translateY(-20px) translateX(0) rotate(0deg);
    opacity: 1;
  }
  100% {
    transform: translateY(110vh) translateX(var(--sway)) rotate(720deg);
    opacity: 0;
  }
}

.petal {
  position: fixed;
  top: 0;
  border-radius: 50% 0 50% 0;
  background: linear-gradient(135deg, #fce4ec, #f8bbd0);
  animation: fall linear infinite;
  pointer-events: none;
}

The --sway CSS custom property is set per-petal from JavaScript, allowing each petal to have its own horizontal drift direction and magnitude without needing separate keyframe definitions.

Responsive Layout

The card is fully responsive — it reads correctly on a phone opened while eating dinner, on a tablet, and on the desktop where it was first shared.

.card {
  max-width: 600px;
  margin: 0 auto;
  padding: clamp(1.5rem, 5vw, 3rem);
  text-align: center;
}

.date {
  font-size: clamp(1.2rem, 4vw, 2rem);
  font-weight: 300;
  letter-spacing: 0.15em;
}

.names {
  font-size: clamp(2rem, 8vw, 4rem);
  font-family: 'Cormorant Garamond', Georgia, serif;
}

clamp() handles the responsive typography without media queries — the font size smoothly scales between the minimum and maximum values based on viewport width. One declaration, infinite screen sizes.

The Interactivity

Subtle details make the card feel alive rather than static:

// Live countdown
function updateCountdown() {
  const ceremony = new Date('2026-04-12T17:00:00');
  const now      = new Date();
  const diff     = ceremony - now;

  if (diff <= 0) {
    document.querySelector('.countdown').textContent = 'Today! 🌸';
    return;
  }

  const days    = Math.floor(diff / 86400000);
  const hours   = Math.floor((diff % 86400000) / 3600000);
  const minutes = Math.floor((diff % 3600000)  / 60000);

  document.querySelector('.days').textContent    = days;
  document.querySelector('.hours').textContent   = hours;
  document.querySelector('.minutes').textContent = minutes;
}

setInterval(updateCountdown, 60000);
updateCountdown();

No Libraries

The entire card — animations, layout, interactivity, countdown — is built with HTML, CSS, and vanilla JavaScript. No jQuery, no React, no animation library, no utility framework.

This is a deliberate choice. The card needs to work in any browser, including on phones that might have outdated software. It needs to load fast on a cellular connection. And it needs to be maintainable by someone who knows the web platform but may not know any particular library.

Zero dependencies means zero dependency problems. The card that worked on the day it was first shared will still work years later, because it relies only on standards that don't change.

License

MIT

Comments