At Invifest we make invitations and guest management for weddings and every big moment. Each family gets its own invitation website: every function with its date, time, venue and map pin, dress codes, the family's own song, and an RSVP for each function. The link goes out on WhatsApp.
That last part changes everything about how we build. The person opening the link is rarely the couple. It is a grandmother on a four year old Android phone, inside WhatsApp's in-app browser, with battery saver on. Here is what that guest taught us.
1. With every animation switched off, the page must still be whole
Reduce Motion on iPhone, Low Power Mode and Android battery savers stop CSS animations dead. If a block only reaches opacity: 1 through a keyframe, it stays invisible forever for that guest. The same settings make scrollIntoView({ behavior: 'smooth' }) do nothing at all.
Our rule now:
- The resting state is visible. Animation is decoration on top, never the thing that reveals content.
- Every reveal driven by an
IntersectionObservergets a timeout fallback that shows the block anyway. - Every automatic scroll has an instant fallback.
And we test it instead of guessing:
await page.emulateMediaFeatures([ { name: 'prefers-reduced-motion', value: 'reduce' }, ]); 2. 100vh is the wrong height on an iPhone
On iOS Safari, 100vh is the height with the toolbar hidden. A "full screen" cover built on it hides its bottom edge, often the button that matters, behind the toolbar. We write every screen-tall block as 100vh first and 100svh right after it (older browsers keep the first), use dvh for fixed overlays, and pad every fixed bottom bar with env(safe-area-inset-bottom) in pages that use viewport-fit=cover. A small lint in our build fails any screen-tall 100vh that has no svh beside it.
3. The WhatsApp preview is the real front door
Most guests decide whether to open the invite from the preview card in the chat. So every invite has its own og:image, title and description, made for that family. When the picture changes we give the file a new name (-v2), because WhatsApp and the CDN both keep the old one for days.
A related trap: never request a new file's URL while the deploy that adds it is still running. A single-page app answers every unknown path with its HTML shell and a 200, and the CDN will happily keep that HTML under your image's name for a week. We check the content type and size of a new asset, not just the status code.
4. One app, many invitations
Every invitation used to be its own small app. Now each one is a folder of words (a short YAML card) plus the family's own files, and one React app builds all of them. Artwork that more than one invitation uses lives once in a shared library and a card points to it, so a fix in one place reaches every invite. A family's own photos and song never leave their folder.
5. A guest should see only their functions
An Indian wedding can have eight functions over three days, and not every guest is invited to all of them. Instead of one invite for everyone, each side or group gets its own link with only its functions, and the RSVP is per function, so the family knows the headcount for the Haldi and the Sangeet separately. We wrote more about this in inviting guests to selected functions.
6. Elders do not install apps or log in
No app, no login, no "open in browser" step. The invite opens straight from the chat, the map pin opens the phone's own maps app, and the text is large enough to read without glasses. Our notes on digital invitations for elderly guests go deeper.
7. Changes should be as easy as saying them
Families ask for changes the way they talk: "make the Sangeet start at 7" or "add Nani's name here". So each family gets a review link where they tap the spot on the invite and simply say what they want. We read it as a list of changes tied to exact places on the page.
If you are building anything that elders open from WhatsApp, these rules carry over. And if you are planning an Indian wedding, here is how an invitation website comes together.
Written by the team at Invifest, India.