Wedding — Rsvp Form
An editorial blush-and-gold wedding RSVP experience that greets guests by name, guides them through attendance, plus-ones, plated meal choices, song requests and dietary notes with airy serif typography, thin gold rules and gentle micro-interactions, then folds shut into a heartfelt confirmation card summarizing every response so couples collect clean replies while guests feel personally welcomed to the celebration.
MCP
Code
:root {
--blush: #e8b7b0;
--blush-50: #fbeeec;
--rose: #c98a86;
--gold: #c9a24b;
--gold-d: #a8862f;
--ink: #3a2b2b;
--ink-2: #6b5555;
--muted: #9a8585;
--bg: #fbf7f4;
--cream: #f4ece4;
--white: #fff;
--line: rgba(58, 43, 43, 0.12);
--r-sm: 8px;
--r-md: 16px;
--r-lg: 26px;
--shadow: 0 24px 60px -28px rgba(58, 43, 43, 0.35);
}
* { box-sizing: border-box; }
html, body { margin: 0; }
body {
font-family: "Inter", system-ui, -apple-system, sans-serif;
line-height: 1.5;
color: var(--ink);
background:
radial-gradient(120% 60% at 50% -10%, var(--blush-50), transparent 60%),
var(--bg);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
min-height: 100vh;
}
.page {
max-width: 640px;
margin: 0 auto;
padding: clamp(24px, 5vw, 56px) 18px 40px;
}
/* ===== Card ===== */
.card {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-lg);
box-shadow: var(--shadow);
padding: clamp(24px, 5vw, 48px);
position: relative;
overflow: hidden;
}
.card::before {
content: "";
position: absolute;
inset: 10px;
border: 1px solid rgba(201, 162, 75, 0.28);
border-radius: calc(var(--r-lg) - 8px);
pointer-events: none;
}
.card > * { position: relative; }
/* ===== Masthead ===== */
.masthead { text-align: center; }
.eyebrow {
font-size: 12px;
letter-spacing: 0.28em;
text-transform: uppercase;
color: var(--rose);
font-weight: 600;
}
.couple {
font-family: "Cormorant Garamond", Georgia, serif;
font-weight: 500;
font-size: clamp(46px, 12vw, 74px);
line-height: 1;
margin: 10px 0 6px;
color: var(--ink);
}
.couple .amp { color: var(--gold); font-style: italic; }
.date, .venue {
margin: 2px 0;
color: var(--ink-2);
font-size: 14px;
}
.venue { color: var(--muted); }
.rule {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
margin: 20px auto;
max-width: 260px;
}
.rule::before, .rule::after {
content: "";
height: 1px;
flex: 1;
background: linear-gradient(90deg, transparent, var(--gold), transparent);
}
.diamond {
width: 8px;
height: 8px;
transform: rotate(45deg);
background: var(--gold);
box-shadow: 0 0 0 3px rgba(201, 162, 75, 0.15);
}
/* ===== Steps / headings ===== */
.step { text-align: center; }
.step-title {
font-family: "Cormorant Garamond", Georgia, serif;
font-weight: 600;
font-size: clamp(24px, 6vw, 30px);
margin: 0 0 4px;
}
.step-note, .block-note {
color: var(--ink-2);
font-size: 14px;
margin: 0 0 16px;
}
/* ===== Fields ===== */
.field { display: block; text-align: left; margin: 0 0 14px; }
.field.grow { flex: 1; }
.field-label {
display: block;
font-size: 12px;
font-weight: 600;
letter-spacing: 0.04em;
color: var(--ink-2);
margin-bottom: 6px;
}
input[type="text"], select, textarea {
width: 100%;
font: inherit;
color: var(--ink);
background: var(--bg);
border: 1px solid var(--line);
border-radius: var(--r-sm);
padding: 12px 14px;
transition: border-color .18s, box-shadow .18s, background .18s;
}
textarea { resize: vertical; }
input::placeholder, textarea::placeholder { color: var(--muted); }
input:hover, select:hover, textarea:hover { border-color: var(--rose); }
input:focus, select:focus, textarea:focus,
.pill-toggle:focus-visible, .meal:focus-visible, .diet:focus-visible,
.btn:focus-visible, .switch input:focus-visible + .switch-track {
outline: none;
border-color: var(--gold);
box-shadow: 0 0 0 3px rgba(201, 162, 75, 0.28);
}
/* ===== Lookup ===== */
.lookup {
display: flex;
gap: 10px;
align-items: flex-end;
max-width: 460px;
margin: 0 auto;
}
.lookup .field { flex: 1; margin: 0; }
.hint { font-size: 12.5px; color: var(--muted); margin: 12px 0 0; text-align: center; }
.lookup-empty {
margin: 14px auto 0;
max-width: 420px;
font-size: 13px;
color: var(--rose);
background: var(--blush-50);
border: 1px solid rgba(201, 138, 134, 0.35);
border-radius: var(--r-sm);
padding: 12px 14px;
}
.lookup-empty a { color: var(--gold-d); }
/* ===== Buttons ===== */
.btn {
font: inherit;
font-weight: 600;
cursor: pointer;
border-radius: 999px;
padding: 12px 22px;
border: 1px solid transparent;
transition: transform .12s, box-shadow .18s, background .18s, color .18s;
white-space: nowrap;
}
.btn:active { transform: translateY(1px); }
.btn-primary {
background: linear-gradient(135deg, var(--gold), var(--gold-d));
color: var(--white);
box-shadow: 0 12px 24px -12px rgba(168, 134, 47, 0.7);
}
.btn-primary:hover { box-shadow: 0 16px 30px -12px rgba(168, 134, 47, 0.85); transform: translateY(-1px); }
.btn-primary:disabled { opacity: .45; cursor: not-allowed; box-shadow: none; transform: none; }
.btn-ghost {
background: var(--white);
border-color: var(--line);
color: var(--ink);
}
.btn-ghost:hover { border-color: var(--gold); color: var(--gold-d); }
.btn-block { width: 100%; padding: 15px; font-size: 15px; }
.btn-text {
background: none;
border: none;
color: var(--muted);
text-decoration: underline;
text-underline-offset: 3px;
padding: 8px;
border-radius: var(--r-sm);
}
.btn-text:hover { color: var(--rose); }
/* ===== Response ===== */
.response { margin-top: 8px; }
.greeting {
text-align: center;
background: var(--cream);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 16px 18px;
margin-bottom: 22px;
}
.greeting .g-name {
font-family: "Cormorant Garamond", Georgia, serif;
font-size: 26px;
color: var(--ink);
display: block;
}
.greeting .g-sub { font-size: 13px; color: var(--ink-2); }
.attend { border: none; margin: 0 0 8px; padding: 0; text-align: center; }
.attend legend { padding: 0; width: 100%; margin-bottom: 14px; }
.toggle {
display: flex;
gap: 12px;
justify-content: center;
flex-wrap: wrap;
}
.pill-toggle {
font: inherit;
font-weight: 600;
cursor: pointer;
background: var(--white);
border: 1px solid var(--line);
border-radius: 999px;
padding: 12px 22px;
color: var(--ink-2);
display: inline-flex;
align-items: center;
gap: 8px;
transition: transform .12s, border-color .18s, background .18s, color .18s, box-shadow .18s;
}
.pill-toggle:hover { border-color: var(--rose); transform: translateY(-1px); }
.pill-toggle .pt-icon { color: var(--gold); }
.pill-toggle[aria-checked="true"] {
background: linear-gradient(135deg, var(--rose), var(--blush));
border-color: var(--rose);
color: var(--white);
box-shadow: 0 12px 24px -14px rgba(201, 138, 134, 0.8);
}
.pill-toggle[aria-checked="true"] .pt-icon { color: var(--white); }
/* ===== Blocks ===== */
.details, .decline-note { margin-top: 22px; }
.block {
padding: 22px 0;
border-top: 1px solid var(--line);
}
.block-head {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 12px;
flex-wrap: wrap;
}
.block-title {
font-family: "Cormorant Garamond", Georgia, serif;
font-weight: 600;
font-size: 22px;
margin: 0 0 4px;
}
.allow-badge {
font-size: 11px;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--gold-d);
background: rgba(201, 162, 75, 0.14);
border: 1px solid rgba(201, 162, 75, 0.35);
padding: 4px 10px;
border-radius: 999px;
}
/* Meals */
.meals { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-top: 6px; }
.meal {
font: inherit;
cursor: pointer;
text-align: center;
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 16px 12px;
display: flex;
flex-direction: column;
gap: 4px;
transition: transform .14s, border-color .18s, box-shadow .18s, background .18s;
}
.meal:hover { transform: translateY(-3px); border-color: var(--rose); box-shadow: 0 14px 26px -18px rgba(201, 138, 134, 0.7); }
.meal-emoji { font-size: 26px; }
.meal-name { font-weight: 700; font-size: 14px; }
.meal-desc { font-size: 11.5px; color: var(--muted); line-height: 1.35; }
.meal[aria-checked="true"] {
border-color: var(--gold);
background: var(--blush-50);
box-shadow: 0 0 0 1px var(--gold) inset, 0 12px 24px -16px rgba(201, 162, 75, 0.7);
}
/* Switch */
.switch {
display: inline-flex;
align-items: center;
gap: 12px;
cursor: pointer;
user-select: none;
margin-top: 6px;
}
.switch input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.switch-track {
width: 46px;
height: 26px;
border-radius: 999px;
background: var(--cream);
border: 1px solid var(--line);
position: relative;
transition: background .2s, border-color .2s;
}
.switch-thumb {
position: absolute;
top: 2px;
left: 2px;
width: 20px;
height: 20px;
border-radius: 50%;
background: var(--white);
box-shadow: 0 2px 5px rgba(58, 43, 43, 0.3);
transition: transform .2s;
}
.switch input:checked + .switch-track {
background: linear-gradient(135deg, var(--gold), var(--gold-d));
border-color: var(--gold-d);
}
.switch input:checked + .switch-track .switch-thumb { transform: translateX(20px); }
.switch-label { font-size: 14px; font-weight: 500; }
.plus-fields {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
margin-top: 16px;
}
.plus-fields .field { margin: 0; }
/* Song */
.song-row { display: flex; gap: 10px; align-items: flex-end; }
.song-row .field { margin: 0; }
.song-list { list-style: none; margin: 14px 0 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.song-list li {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
background: var(--cream);
border: 1px solid var(--line);
border-radius: var(--r-sm);
padding: 10px 14px;
font-size: 14px;
animation: pop .2s ease;
}
.song-list .note { color: var(--gold-d); margin-right: auto; }
.song-list button {
background: none;
border: none;
cursor: pointer;
color: var(--muted);
font-size: 18px;
line-height: 1;
border-radius: 6px;
padding: 2px 6px;
}
.song-list button:hover { color: var(--rose); }
@keyframes pop { from { opacity: 0; transform: translateY(-4px); } }
/* Dietary */
.diet-pills { display: flex; flex-wrap: wrap; gap: 8px; margin: 6px 0 16px; }
.diet {
font: inherit;
font-size: 13px;
font-weight: 500;
cursor: pointer;
background: var(--white);
border: 1px solid var(--line);
border-radius: 999px;
padding: 8px 15px;
color: var(--ink-2);
transition: transform .12s, border-color .18s, background .18s, color .18s;
}
.diet:hover { border-color: var(--rose); transform: translateY(-1px); }
.diet[aria-pressed="true"] {
background: var(--rose);
border-color: var(--rose);
color: var(--white);
}
/* Footer */
.response-foot { margin-top: 24px; text-align: center; display: flex; flex-direction: column; gap: 6px; }
/* ===== Confirmation ===== */
.confirm { text-align: center; padding: 8px 0; animation: fade .4s ease; }
@keyframes fade { from { opacity: 0; transform: translateY(8px); } }
.monogram {
width: 84px;
height: 84px;
margin: 0 auto 18px;
border-radius: 50%;
display: grid;
place-items: center;
font-family: "Cormorant Garamond", Georgia, serif;
font-size: 26px;
color: var(--gold-d);
background: var(--blush-50);
border: 1px solid rgba(201, 162, 75, 0.4);
box-shadow: 0 0 0 6px rgba(201, 162, 75, 0.08);
}
.confirm-title {
font-family: "Cormorant Garamond", Georgia, serif;
font-weight: 600;
font-size: clamp(32px, 8vw, 44px);
margin: 0 0 6px;
}
.confirm-sub { color: var(--ink-2); margin: 0 auto 20px; max-width: 380px; }
.summary {
text-align: left;
max-width: 420px;
margin: 0 auto;
display: grid;
grid-template-columns: auto 1fr;
gap: 10px 18px;
font-size: 14px;
}
.summary dt { color: var(--muted); font-weight: 600; }
.summary dd { margin: 0; color: var(--ink); }
.summary dd .tag {
display: inline-block;
background: var(--cream);
border: 1px solid var(--line);
border-radius: 6px;
padding: 1px 8px;
margin: 2px 4px 2px 0;
font-size: 12.5px;
}
.confirm-count { font-size: 13px; color: var(--muted); margin: 8px 0 10px; }
.foot-credit {
text-align: center;
margin: 24px 0 0;
font-size: 12.5px;
letter-spacing: 0.08em;
color: var(--muted);
}
/* ===== Toast ===== */
.toast {
position: fixed;
left: 50%;
bottom: 26px;
transform: translate(-50%, 20px);
background: var(--ink);
color: var(--white);
padding: 12px 20px;
border-radius: 999px;
font-size: 14px;
font-weight: 500;
box-shadow: 0 16px 34px -16px rgba(58, 43, 43, 0.7);
opacity: 0;
pointer-events: none;
transition: opacity .25s, transform .25s;
z-index: 50;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }
.toast::before { content: "♥ "; color: var(--blush); }
/* ===== Responsive ===== */
@media (max-width: 520px) {
.card { padding: 22px 16px; }
.lookup { flex-direction: column; align-items: stretch; }
.lookup .btn { width: 100%; }
.meals { grid-template-columns: 1fr; }
.plus-fields { grid-template-columns: 1fr; }
.song-row { flex-direction: column; align-items: stretch; }
.song-row .btn { width: 100%; }
.toggle { flex-direction: column; }
.pill-toggle { justify-content: center; }
.summary { grid-template-columns: 1fr; gap: 2px 0; }
.summary dt { margin-top: 8px; }
}(function () {
"use strict";
/* ---- Fictional guest list ---- */
var GUESTS = [
{ name: "Eleanor Whitfield", party: "The Whitfield Family", seats: 2 },
{ name: "Marcus Bell", party: "Mr. Marcus Bell", seats: 1 },
{ name: "Priya Anand", party: "Priya & Guest", seats: 2 },
{ name: "Daniel & Sofia Reyes", party: "The Reyes Family", seats: 2 },
{ name: "Harriet Osei", party: "Harriet Osei", seats: 1 }
];
var state = {
guest: null,
attend: null,
meal: null,
plusOne: false,
songs: [],
diet: []
};
var replies = 47; // running fictional count
/* ---- Helpers ---- */
function $(id) { return document.getElementById(id); }
function norm(s) { return (s || "").trim().toLowerCase().replace(/\s+/g, " "); }
var toastEl = $("toast");
var toastTimer;
function toast(msg) {
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () { toastEl.classList.remove("show"); }, 2600);
}
function updateSubmit() {
var ok = state.attend === "no" || (state.attend === "yes" && !!state.meal);
$("submitBtn").disabled = !ok;
}
/* ---- Lookup ---- */
$("lookupForm").addEventListener("submit", function (e) {
e.preventDefault();
var q = norm($("lookupName").value);
var empty = $("lookupEmpty");
if (!q) { $("lookupName").focus(); return; }
var match = GUESTS.find(function (g) {
var n = norm(g.name);
return n === q || n.indexOf(q) !== -1 || q.indexOf(n.split(" ")[0]) === 0;
});
if (!match) {
empty.hidden = false;
return;
}
empty.hidden = true;
state.guest = match;
$("greeting").innerHTML =
'<span class="g-name">Welcome, ' + match.name + "</span>" +
'<span class="g-sub">' + match.party + " · " +
match.seats + (match.seats > 1 ? " seats reserved" : " seat reserved") + "</span>";
// plus-one availability
var plusBlock = $("plusOneBlock");
if (match.seats > 1) {
plusBlock.style.display = "";
$("allowBadge").textContent = (match.seats - 1) + " extra seat";
} else {
plusBlock.style.display = "none";
}
$("lookupStep").hidden = true;
$("responseForm").hidden = false;
toast("Invitation found — welcome " + match.name.split(" ")[0] + "!");
$("responseForm").scrollIntoView({ behavior: "smooth", block: "start" });
});
/* ---- Attend toggle ---- */
document.querySelectorAll(".pill-toggle").forEach(function (btn) {
btn.addEventListener("click", function () {
state.attend = btn.dataset.attend;
document.querySelectorAll(".pill-toggle").forEach(function (b) {
b.setAttribute("aria-checked", b === btn ? "true" : "false");
});
var attending = state.attend === "yes";
$("details").hidden = !attending;
$("declineNote").hidden = attending;
$("submitBtn").textContent = attending ? "Send RSVP" : "Send Regrets";
updateSubmit();
});
});
/* ---- Meal choice ---- */
document.querySelectorAll(".meal").forEach(function (btn) {
btn.addEventListener("click", function () {
state.meal = btn.dataset.meal;
document.querySelectorAll(".meal").forEach(function (b) {
b.setAttribute("aria-checked", b === btn ? "true" : "false");
});
updateSubmit();
});
});
/* ---- Plus one ---- */
$("plusOneToggle").addEventListener("change", function () {
state.plusOne = this.checked;
$("plusFields").hidden = !this.checked;
});
/* ---- Songs ---- */
function addSong() {
var input = $("songInput");
var val = input.value.trim();
if (!val) { input.focus(); return; }
if (state.songs.length >= 5) { toast("Five songs is plenty — thank you!"); return; }
state.songs.push(val);
input.value = "";
renderSongs();
toast("Added to the dance-floor wishlist");
}
function renderSongs() {
var ul = $("songList");
ul.innerHTML = "";
state.songs.forEach(function (s, i) {
var li = document.createElement("li");
li.innerHTML = '<span class="note">♪</span><span style="margin-right:auto">' +
s + '</span><button type="button" aria-label="Remove ' + s + '">×</button>';
li.querySelector("button").addEventListener("click", function () {
state.songs.splice(i, 1);
renderSongs();
});
ul.appendChild(li);
});
}
$("addSong").addEventListener("click", addSong);
$("songInput").addEventListener("keydown", function (e) {
if (e.key === "Enter") { e.preventDefault(); addSong(); }
});
/* ---- Dietary ---- */
document.querySelectorAll(".diet").forEach(function (btn) {
btn.setAttribute("aria-pressed", "false");
btn.addEventListener("click", function () {
var on = btn.getAttribute("aria-pressed") === "true";
btn.setAttribute("aria-pressed", on ? "false" : "true");
var d = btn.dataset.diet;
if (on) { state.diet = state.diet.filter(function (x) { return x !== d; }); }
else { state.diet.push(d); }
});
});
/* ---- Submit ---- */
$("responseForm").addEventListener("submit", function (e) {
e.preventDefault();
if ($("submitBtn").disabled) return;
var attending = state.attend === "yes";
var g = state.guest;
$("confirmSub").innerHTML = attending
? "We can’t wait to celebrate with you, " + g.name.split(" ")[0] +
". Your response has been noted below."
: "We’ll miss you, " + g.name.split(" ")[0] +
", but thank you for letting us know. Sending love.";
var rows = [["Guest", g.name]];
rows.push(["Attending", attending ? "Joyfully accepts ♥" : "Regretfully declines"]);
if (attending) {
rows.push(["Entrée", state.meal || "—"]);
if (state.plusOne) {
var pn = $("plusName").value.trim() || "Guest";
var pm = $("plusMeal").value || "—";
rows.push(["Plus-one", pn + " · " + pm]);
}
if (state.songs.length) {
rows.push(["Song requests",
state.songs.map(function (s) { return '<span class="tag">' + s + "</span>"; }).join("")]);
}
if (state.diet.length) {
rows.push(["Dietary",
state.diet.map(function (d) { return '<span class="tag">' + d + "</span>"; }).join("")]);
}
var note = $("dietNote").value.trim();
if (note) rows.push(["Notes", note]);
} else {
var wish = $("wishNote").value.trim();
if (wish) rows.push(["Message", wish]);
}
var dl = $("summary");
dl.innerHTML = "";
rows.forEach(function (r) {
var dt = document.createElement("dt"); dt.textContent = r[0];
var dd = document.createElement("dd"); dd.innerHTML = r[1];
dl.appendChild(dt); dl.appendChild(dd);
});
replies += 1;
$("confirmCount").textContent = replies + " guests have responded so far.";
$("responseForm").hidden = true;
$("lookupStep").hidden = true;
$("confirm").hidden = false;
$("confirm").scrollIntoView({ behavior: "smooth", block: "start" });
toast(attending ? "RSVP sent — see you there!" : "Regrets sent — thank you");
});
/* ---- Edit / restart ---- */
$("editAnswer").addEventListener("click", function () {
$("confirm").hidden = true;
$("responseForm").hidden = false;
$("responseForm").scrollIntoView({ behavior: "smooth", block: "start" });
});
function reset() {
state = { guest: null, attend: null, meal: null, plusOne: false, songs: [], diet: [] };
$("responseForm").reset();
document.querySelectorAll(".pill-toggle").forEach(function (b) { b.setAttribute("aria-checked", "false"); });
document.querySelectorAll(".meal").forEach(function (b) { b.setAttribute("aria-checked", "false"); });
document.querySelectorAll(".diet").forEach(function (b) { b.setAttribute("aria-pressed", "false"); });
$("details").hidden = true;
$("declineNote").hidden = true;
$("plusFields").hidden = true;
renderSongs();
updateSubmit();
$("confirm").hidden = true;
$("responseForm").hidden = true;
$("lookupStep").hidden = false;
$("lookupName").value = "";
$("lookupName").focus();
}
$("startOver").addEventListener("click", reset);
updateSubmit();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Amelia & Julian — RSVP</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@400;500;600&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main class="page">
<article class="card" id="rsvpCard">
<!-- ===== Header ===== -->
<header class="masthead">
<span class="eyebrow">Together with their families</span>
<h1 class="couple">Amelia <span class="amp">&</span> Julian</h1>
<p class="date">Saturday, the twelfth of September · Two thousand twenty-six</p>
<p class="venue">Rosewood Orchard · Sonoma, California</p>
<div class="rule" aria-hidden="true"><span class="diamond"></span></div>
</header>
<!-- ===== Lookup ===== -->
<section class="step" id="lookupStep" aria-labelledby="lookupTitle">
<h2 class="step-title" id="lookupTitle">Find your invitation</h2>
<p class="step-note">Enter the name on your envelope to begin.</p>
<form class="lookup" id="lookupForm" novalidate>
<label class="field">
<span class="field-label">Guest name</span>
<input type="text" id="lookupName" name="lookupName" autocomplete="name"
placeholder="e.g. Eleanor Whitfield" aria-describedby="lookupHint" />
</label>
<button class="btn btn-primary" type="submit">Look up</button>
</form>
<p class="hint" id="lookupHint">Try “Eleanor Whitfield”, “Marcus Bell” or “Priya Anand”.</p>
<p class="lookup-empty" id="lookupEmpty" hidden role="alert">
We couldn’t find that name. Check the spelling on your invitation, or reach the couple at
<a href="mailto:[email protected]">[email protected]</a>.
</p>
</section>
<!-- ===== Response ===== -->
<form class="response" id="responseForm" hidden novalidate>
<div class="greeting" id="greeting"></div>
<fieldset class="attend">
<legend class="step-title">Will you be joining us?</legend>
<div class="toggle" role="radiogroup" aria-label="Attending">
<button type="button" class="pill-toggle" data-attend="yes" role="radio" aria-checked="false">
<span class="pt-icon">♥</span> Joyfully accepts
</button>
<button type="button" class="pill-toggle" data-attend="no" role="radio" aria-checked="false">
<span class="pt-icon">✎</span> Regretfully declines
</button>
</div>
</fieldset>
<!-- Attending-only details -->
<div class="details" id="details" hidden>
<!-- Meal -->
<section class="block" aria-labelledby="mealTitle">
<h3 class="block-title" id="mealTitle">Choose your entrée</h3>
<div class="meals" id="meals" role="radiogroup" aria-label="Meal choice">
<button type="button" class="meal" data-meal="Herb-Roasted Chicken" role="radio" aria-checked="false">
<span class="meal-emoji" aria-hidden="true">🍗</span>
<span class="meal-name">Herb-Roasted Chicken</span>
<span class="meal-desc">Lemon thyme jus, spring vegetables</span>
</button>
<button type="button" class="meal" data-meal="Seared Salmon" role="radio" aria-checked="false">
<span class="meal-emoji" aria-hidden="true">🐟</span>
<span class="meal-name">Seared Salmon</span>
<span class="meal-desc">Citrus beurre blanc, fennel</span>
</button>
<button type="button" class="meal" data-meal="Wild Mushroom Risotto" role="radio" aria-checked="false">
<span class="meal-emoji" aria-hidden="true">🍄</span>
<span class="meal-name">Wild Mushroom Risotto</span>
<span class="meal-desc">Truffle, aged parmesan · vegetarian</span>
</button>
</div>
</section>
<!-- Plus one -->
<section class="block" id="plusOneBlock" aria-labelledby="plusTitle">
<div class="block-head">
<h3 class="block-title" id="plusTitle">Bringing a guest?</h3>
<span class="allow-badge" id="allowBadge">1 seat reserved</span>
</div>
<label class="switch">
<input type="checkbox" id="plusOneToggle" />
<span class="switch-track" aria-hidden="true"><span class="switch-thumb"></span></span>
<span class="switch-label">Yes, I’ll bring a plus-one</span>
</label>
<div class="plus-fields" id="plusFields" hidden>
<label class="field">
<span class="field-label">Their name</span>
<input type="text" id="plusName" placeholder="Guest’s full name" />
</label>
<label class="field">
<span class="field-label">Their entrée</span>
<select id="plusMeal">
<option value="">Select a dish…</option>
<option>Herb-Roasted Chicken</option>
<option>Seared Salmon</option>
<option>Wild Mushroom Risotto</option>
</select>
</label>
</div>
</section>
<!-- Song -->
<section class="block" aria-labelledby="songTitle">
<h3 class="block-title" id="songTitle">Request a song</h3>
<p class="block-note">Help us build the dance-floor wishlist.</p>
<div class="song-row">
<label class="field grow">
<span class="field-label">Song & artist</span>
<input type="text" id="songInput" placeholder="e.g. At Last — Etta James" />
</label>
<button type="button" class="btn btn-ghost" id="addSong">Add</button>
</div>
<ul class="song-list" id="songList" aria-live="polite"></ul>
</section>
<!-- Dietary -->
<section class="block" aria-labelledby="dietTitle">
<h3 class="block-title" id="dietTitle">Dietary needs</h3>
<div class="diet-pills" id="dietPills">
<button type="button" class="diet" data-diet="Vegetarian">Vegetarian</button>
<button type="button" class="diet" data-diet="Vegan">Vegan</button>
<button type="button" class="diet" data-diet="Gluten-free">Gluten-free</button>
<button type="button" class="diet" data-diet="Nut allergy">Nut allergy</button>
<button type="button" class="diet" data-diet="Dairy-free">Dairy-free</button>
<button type="button" class="diet" data-diet="Shellfish allergy">Shellfish allergy</button>
</div>
<label class="field">
<span class="field-label">Anything else we should know?</span>
<textarea id="dietNote" rows="2" placeholder="Notes for our caterer…"></textarea>
</label>
</section>
</div>
<!-- Declining note -->
<div class="decline-note" id="declineNote" hidden>
<label class="field">
<span class="field-label">Leave the couple a note</span>
<textarea id="wishNote" rows="3" placeholder="We’ll miss you — send your best wishes."></textarea>
</label>
</div>
<footer class="response-foot">
<button class="btn btn-primary btn-block" type="submit" id="submitBtn" disabled>Send RSVP</button>
<button class="btn btn-text" type="button" id="startOver">Look up a different name</button>
</footer>
</form>
<!-- ===== Confirmation ===== -->
<section class="confirm" id="confirm" hidden aria-live="polite">
<div class="monogram" aria-hidden="true">A&J</div>
<h2 class="confirm-title" id="confirmTitle">Thank you!</h2>
<p class="confirm-sub" id="confirmSub"></p>
<dl class="summary" id="summary"></dl>
<div class="rule" aria-hidden="true"><span class="diamond"></span></div>
<p class="confirm-count" id="confirmCount"></p>
<button class="btn btn-text" type="button" id="editAnswer">Change my answer</button>
</section>
</article>
<p class="foot-credit">With love, Amelia & Julian · #TheOrchardVows</p>
</main>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>A romantic, single-page RSVP form set in a blush-and-gold editorial palette with Cormorant Garamond headings and Inter body text. Guests begin by looking up their invitation by name against a small fictional guest list; a match reveals their party details and pre-fills the response card, while a graceful empty state nudges typos. The attending toggle expands or collapses the meal, plus-one, song and dietary sections so the form only ever asks what is relevant.
Attending guests choose plated entrées from illustrated option cards, add a plus-one with their own meal, drop a song request onto the dance-floor wishlist, and flag dietary needs with quick pill toggles plus a free-text note. Thin gold rules, hover lifts and focus rings keep every control keyboard-usable and accessible.
On submit the card flips into an elegant confirmation that echoes the guest’s name and every choice back to them, complete with a decorative monogram and a toast. A subtle count of replies and an editable “change my answer” link round out the interaction, making the demo feel like a real wedding site component rather than a placeholder.