UI Components Easy
Post-Meal Feedback Form
Post-meal NPS and experience form: star ratings per dimension (food, service, ambiance), an emoji mood scale, a free-text comment, and a thank-you confirmation screen.
Open in Lab
MCP
html css vanilla-js
Targets: JS HTML
Code
/* ===================================================
Post-Meal Feedback Form โ Phase 27 Restaurant Theme
=================================================== */
:root {
--cream: #FAF7F1;
--ink: #2C1A0E;
--forest: #345F40;
--forest-d: #213D29;
--terracotta: #C4622D;
--gold: #D4A853;
--warm-gray: #8A7D72;
--bone: #F0EBE0;
--font-heading: 'Playfair Display', Georgia, serif;
--font-body: 'Inter', system-ui, sans-serif;
--radius-sm: 8px;
--radius-md: 14px;
--radius-lg: 20px;
--radius-full: 9999px;
--shadow-card: 0 8px 32px rgba(44, 26, 14, 0.12), 0 2px 8px rgba(44, 26, 14, 0.06);
--shadow-btn: 0 2px 8px rgba(52, 95, 64, 0.25);
--transition-fast: 150ms ease;
--transition-base: 250ms ease;
}
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: var(--font-body);
background-color: var(--bone);
background-image:
radial-gradient(ellipse at 20% 0%, rgba(212, 168, 83, 0.15) 0%, transparent 55%),
radial-gradient(ellipse at 80% 100%, rgba(52, 95, 64, 0.12) 0%, transparent 55%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 24px 16px;
color: var(--ink);
}
/* ---- Page Wrapper ---- */
.page-wrapper {
width: 100%;
max-width: 440px;
}
/* ---- Card ---- */
.card {
background: var(--cream);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-card);
overflow: hidden;
padding: 0;
}
/* ---- Progress Bar ---- */
.progress-bar {
padding: 28px 32px 0;
}
.progress-track {
position: relative;
}
.progress-line {
position: absolute;
top: 10px;
left: 10px;
right: 10px;
height: 2px;
background: var(--bone);
border-radius: var(--radius-full);
overflow: hidden;
z-index: 0;
}
.progress-fill {
height: 100%;
background: var(--forest);
border-radius: var(--radius-full);
transition: width 0.4s cubic-bezier(0.4, 0, 0.2, 1);
width: 0%;
}
.progress-dots {
display: flex;
justify-content: space-between;
position: relative;
z-index: 1;
}
.progress-dot {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
cursor: default;
}
.dot-circle {
display: block;
width: 20px;
height: 20px;
border-radius: 50%;
background: var(--bone);
border: 2px solid var(--bone);
transition: background var(--transition-base), border-color var(--transition-base), transform var(--transition-base);
box-shadow: 0 0 0 0 transparent;
}
.progress-dot.active .dot-circle {
background: var(--forest);
border-color: var(--forest);
transform: scale(1.15);
box-shadow: 0 0 0 3px rgba(52, 95, 64, 0.18);
}
.progress-dot.completed .dot-circle {
background: var(--forest);
border-color: var(--forest);
}
.dot-label {
font-size: 10px;
font-weight: 500;
color: var(--warm-gray);
letter-spacing: 0.04em;
text-transform: uppercase;
transition: color var(--transition-base);
}
.progress-dot.active .dot-label,
.progress-dot.completed .dot-label {
color: var(--forest);
}
/* ---- Step Layout ---- */
.step {
padding: 28px 32px 32px;
animation: step-in 0.3s ease both;
}
@keyframes step-in {
from {
opacity: 0;
transform: translateY(12px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.step.hidden {
display: none;
}
/* ---- Step Header ---- */
.step-eyebrow {
font-family: var(--font-body);
font-size: 11px;
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--warm-gray);
margin-bottom: 8px;
}
.step-title {
font-family: var(--font-heading);
font-size: 1.7rem;
font-weight: 700;
color: var(--ink);
line-height: 1.2;
margin-bottom: 8px;
}
.step-subtitle {
font-size: 0.875rem;
color: var(--warm-gray);
line-height: 1.5;
margin-bottom: 28px;
}
/* ---- Emoji Row ---- */
.emoji-row {
display: flex;
justify-content: space-between;
gap: 6px;
margin-bottom: 8px;
}
.emoji-btn {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
background: var(--bone);
border: 2px solid transparent;
border-radius: var(--radius-md);
padding: 14px 6px 12px;
cursor: pointer;
transition:
background var(--transition-fast),
border-color var(--transition-fast),
transform var(--transition-fast),
box-shadow var(--transition-fast);
-webkit-tap-highlight-color: transparent;
}
.emoji-btn:hover {
background: #E8E0D2;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(44, 26, 14, 0.08);
}
.emoji-btn:active {
transform: translateY(0);
}
.emoji-btn.selected {
background: var(--cream);
border-color: var(--gold);
box-shadow: 0 0 0 3px rgba(212, 168, 83, 0.2), 0 4px 16px rgba(44, 26, 14, 0.1);
transform: translateY(-2px) scale(1.04);
}
.emoji-icon {
font-size: 2rem;
line-height: 1;
display: block;
}
.emoji-label {
font-size: 10px;
font-weight: 600;
color: var(--warm-gray);
text-align: center;
letter-spacing: 0.02em;
}
.emoji-btn.selected .emoji-label {
color: var(--ink);
}
/* ---- Star Rating Rows ---- */
.rating-rows {
display: flex;
flex-direction: column;
gap: 0;
margin-bottom: 24px;
border: 1px solid var(--bone);
border-radius: var(--radius-md);
overflow: hidden;
}
.rating-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 16px;
background: var(--cream);
border-bottom: 1px solid var(--bone);
transition: background var(--transition-fast);
}
.rating-row:last-child {
border-bottom: none;
}
.rating-row:hover {
background: #F5F1EA;
}
.rating-label {
font-size: 0.9rem;
font-weight: 600;
color: var(--ink);
flex: 1;
}
.stars {
display: flex;
gap: 4px;
}
.star-btn {
background: none;
border: none;
cursor: pointer;
font-size: 1.5rem;
color: var(--warm-gray);
line-height: 1;
padding: 2px 3px;
transition: color var(--transition-fast), transform var(--transition-fast);
-webkit-tap-highlight-color: transparent;
user-select: none;
}
.star-btn:hover,
.star-btn.hover-active {
color: var(--gold);
transform: scale(1.1);
}
.star-btn.selected {
color: var(--gold);
}
/* ---- Validation ---- */
.validation-msg {
font-size: 0.8rem;
color: var(--terracotta);
margin-bottom: 12px;
font-weight: 500;
}
.validation-msg.hidden {
display: none;
}
/* ---- Text Fields ---- */
.field-group {
margin-bottom: 20px;
}
.field-label {
display: block;
font-size: 0.875rem;
font-weight: 600;
color: var(--ink);
margin-bottom: 8px;
}
.field-optional {
font-weight: 400;
color: var(--warm-gray);
}
.textarea,
.input {
width: 100%;
background: var(--bone);
border: 1.5px solid transparent;
border-radius: var(--radius-sm);
padding: 12px 14px;
font-family: var(--font-body);
font-size: 0.9rem;
color: var(--ink);
resize: none;
transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
outline: none;
line-height: 1.5;
}
.textarea::placeholder,
.input::placeholder {
color: var(--warm-gray);
}
.textarea:focus,
.input:focus {
border-color: var(--forest);
box-shadow: 0 0 0 3px rgba(52, 95, 64, 0.12);
background: var(--cream);
}
.char-count {
font-size: 11px;
color: var(--warm-gray);
text-align: right;
margin-top: 6px;
}
/* ---- Checkbox ---- */
.checkbox-row {
display: flex;
align-items: flex-start;
gap: 10px;
cursor: pointer;
user-select: none;
}
.checkbox-row input[type="checkbox"] {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.checkbox-custom {
flex-shrink: 0;
width: 20px;
height: 20px;
border: 2px solid var(--warm-gray);
border-radius: 5px;
background: var(--bone);
display: flex;
align-items: center;
justify-content: center;
margin-top: 1px;
transition: border-color var(--transition-fast), background var(--transition-fast);
}
.checkbox-row input[type="checkbox"]:checked + .checkbox-custom {
background: var(--forest);
border-color: var(--forest);
}
.checkbox-row input[type="checkbox"]:checked + .checkbox-custom::after {
content: 'โ';
color: var(--cream);
font-size: 13px;
font-weight: 700;
line-height: 1;
}
.checkbox-text {
font-size: 0.875rem;
color: var(--ink);
line-height: 1.45;
}
.email-field {
margin-top: 14px;
}
.email-field.hidden {
display: none;
}
.field-hint {
font-size: 11px;
color: var(--warm-gray);
margin-top: 6px;
line-height: 1.4;
}
/* ---- Buttons ---- */
.btn-row {
display: flex;
gap: 10px;
margin-top: 4px;
}
.btn {
flex: 1;
padding: 13px 20px;
border-radius: var(--radius-full);
font-family: var(--font-body);
font-size: 0.9rem;
font-weight: 600;
letter-spacing: 0.02em;
cursor: pointer;
border: none;
transition:
background var(--transition-fast),
color var(--transition-fast),
transform var(--transition-fast),
box-shadow var(--transition-fast);
-webkit-tap-highlight-color: transparent;
}
.btn-primary {
background: var(--forest);
color: var(--cream);
box-shadow: var(--shadow-btn);
}
.btn-primary:hover {
background: var(--forest-d);
transform: translateY(-1px);
box-shadow: 0 4px 14px rgba(52, 95, 64, 0.35);
}
.btn-primary:active {
transform: translateY(0);
box-shadow: var(--shadow-btn);
}
.btn-ghost {
background: transparent;
color: var(--warm-gray);
border: 1.5px solid var(--bone);
flex: 0 0 90px;
}
.btn-ghost:hover {
background: var(--bone);
color: var(--ink);
border-color: #D8D0C4;
}
.btn-full {
width: 100%;
flex: none;
}
/* ---- Thank You Screen ---- */
.thankyou-content {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
padding-top: 8px;
}
.checkmark-circle {
width: 72px;
height: 72px;
border-radius: 50%;
background: var(--forest);
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20px;
animation: scale-in 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
@keyframes scale-in {
from {
opacity: 0;
transform: scale(0.4);
}
to {
opacity: 1;
transform: scale(1);
}
}
.checkmark-icon {
font-size: 2rem;
color: var(--bone);
font-weight: 700;
line-height: 1;
}
.thankyou-title {
font-family: var(--font-heading);
font-size: 2rem;
font-weight: 800;
color: var(--ink);
margin-bottom: 10px;
}
.thankyou-subtitle {
font-size: 0.9rem;
color: var(--warm-gray);
line-height: 1.5;
margin-bottom: 28px;
max-width: 300px;
}
.summary-block {
width: 100%;
background: var(--bone);
border-radius: var(--radius-md);
padding: 18px 20px;
margin-bottom: 24px;
text-align: left;
}
.summary-mood {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 14px;
padding-bottom: 14px;
border-bottom: 1px solid rgba(44, 26, 14, 0.08);
}
.summary-mood-emoji {
font-size: 1.75rem;
line-height: 1;
}
.summary-mood-text {
font-size: 0.9rem;
font-weight: 600;
color: var(--ink);
}
.summary-mood-sub {
font-size: 0.8rem;
color: var(--warm-gray);
}
.summary-chips {
display: flex;
flex-direction: column;
gap: 8px;
}
.summary-chip {
display: flex;
align-items: center;
justify-content: space-between;
}
.chip-label {
font-size: 0.825rem;
font-weight: 600;
color: var(--warm-gray);
min-width: 90px;
}
.chip-stars {
font-size: 1rem;
color: var(--gold);
letter-spacing: 1px;
}
.chip-stars .empty {
color: #D8D0C4;
}
/* ---- Responsive ---- */
@media (max-width: 380px) {
.step {
padding: 22px 20px 26px;
}
.progress-bar {
padding: 22px 20px 0;
}
.step-title {
font-size: 1.4rem;
}
.emoji-icon {
font-size: 1.6rem;
}
.emoji-label {
font-size: 9px;
}
}/* =============================================
Post-Meal Feedback Form โ Interactive Logic
============================================= */
(function () {
'use strict';
// ---- State ----
const state = {
step: 1,
mood: null,
ratings: { food: 0, service: 0, ambiance: 0, value: 0 },
comment: '',
contact: false,
email: '',
};
const MOOD_LABELS = {
terrible: { emoji: '๐', label: 'Terrible', sub: 'We\'re sorry to hear that' },
poor: { emoji: '๐', label: 'Poor', sub: 'We\'ll work to do better' },
okay: { emoji: '๐', label: 'Okay', sub: 'Thanks for letting us know' },
great: { emoji: '๐', label: 'Great', sub: 'So glad you enjoyed it!' },
amazing: { emoji: '๐คฉ', label: 'Amazing', sub: 'We\'re thrilled to hear that!' },
};
const DIM_LABELS = {
food: 'Food Quality',
service: 'Service',
ambiance: 'Ambiance',
value: 'Value',
};
// ---- Element references ----
const $ = (sel, ctx) => (ctx || document).querySelector(sel);
const $$ = (sel, ctx) => [...(ctx || document).querySelectorAll(sel)];
const steps = {
1: $('#step-1'),
2: $('#step-2'),
3: $('#step-3'),
4: $('#step-4'),
};
const progressDots = $$('.progress-dot');
const progressFill = $('#progress-fill');
// ---- Progress helpers ----
function updateProgress(currentStep) {
// Progress fill: 0% at step 1, 33% at step 2, 66% at step 3, 100% at step 4
const pct = ((currentStep - 1) / 3) * 100;
progressFill.style.width = pct + '%';
progressDots.forEach((dot) => {
const dotStep = parseInt(dot.dataset.step, 10);
dot.classList.toggle('active', dotStep === currentStep);
dot.classList.toggle('completed', dotStep < currentStep);
});
}
// ---- Step navigation ----
function goToStep(nextStep) {
const current = steps[state.step];
const next = steps[nextStep];
if (!next) return;
// Hide current, show next
if (current) current.classList.add('hidden');
// Force reflow so animation re-triggers
next.classList.remove('hidden');
void next.offsetWidth;
state.step = nextStep;
updateProgress(nextStep);
// Special setup for step 4
if (nextStep === 4) renderSummary();
}
// ---- Step 1: Emoji mood ----
$$('.emoji-btn').forEach((btn) => {
btn.addEventListener('click', () => {
state.mood = btn.dataset.mood;
$$('.emoji-btn').forEach((b) => b.classList.remove('selected'));
btn.classList.add('selected');
// Auto-advance after brief delay
setTimeout(() => goToStep(2), 400);
});
});
// ---- Step 2: Star ratings ----
$$('.stars').forEach((starGroup) => {
const dimension = starGroup.dataset.dimension;
const starBtns = $$('.star-btn', starGroup);
starBtns.forEach((star, idx) => {
const value = parseInt(star.dataset.value, 10);
// Click
star.addEventListener('click', () => {
state.ratings[dimension] = value;
updateStarDisplay(starGroup, value);
hideStep2Error();
});
// Hover
star.addEventListener('mouseenter', () => {
highlightStarsUpTo(starBtns, idx);
});
star.addEventListener('mouseleave', () => {
resetStarHover(starBtns, state.ratings[dimension]);
});
});
});
function updateStarDisplay(group, selectedValue) {
$$('.star-btn', group).forEach((btn) => {
const val = parseInt(btn.dataset.value, 10);
btn.classList.toggle('selected', val <= selectedValue);
btn.classList.remove('hover-active');
// Swap character
btn.textContent = val <= selectedValue ? 'โ
' : 'โ';
});
}
function highlightStarsUpTo(btns, hoverIdx) {
btns.forEach((btn, idx) => {
if (idx <= hoverIdx) {
btn.classList.add('hover-active');
btn.textContent = 'โ
';
} else {
btn.classList.remove('hover-active');
const dim = btn.closest('.stars').dataset.dimension;
btn.textContent = parseInt(btn.dataset.value, 10) <= state.ratings[dim] ? 'โ
' : 'โ';
}
});
}
function resetStarHover(btns, selectedValue) {
btns.forEach((btn) => {
btn.classList.remove('hover-active');
const val = parseInt(btn.dataset.value, 10);
btn.textContent = val <= selectedValue ? 'โ
' : 'โ';
});
}
function hideStep2Error() {
$('#step2-error').classList.add('hidden');
}
// Step 2 back/next
$('#back-2').addEventListener('click', () => goToStep(1));
$('#next-2').addEventListener('click', () => {
const anyRated = Object.values(state.ratings).some((v) => v > 0);
if (!anyRated) {
$('#step2-error').classList.remove('hidden');
return;
}
goToStep(3);
});
// ---- Step 3: Comment + contact ----
const commentInput = $('#comment-input');
const charUsed = $('#char-used');
const contactCheckbox = $('#contact-checkbox');
const emailField = $('#email-field');
const emailInput = $('#email-input');
commentInput.addEventListener('input', () => {
state.comment = commentInput.value;
charUsed.textContent = commentInput.value.length;
});
contactCheckbox.addEventListener('change', () => {
state.contact = contactCheckbox.checked;
emailField.classList.toggle('hidden', !contactCheckbox.checked);
if (contactCheckbox.checked) emailInput.focus();
});
emailInput.addEventListener('input', () => {
state.email = emailInput.value;
});
$('#back-3').addEventListener('click', () => goToStep(2));
$('#submit-btn').addEventListener('click', () => {
goToStep(4);
});
// ---- Step 4: Summary render ----
function renderSummary() {
const moodData = MOOD_LABELS[state.mood] || { emoji: '๐', label: 'No mood selected', sub: '' };
// Mood block
const summaryMood = $('#summary-mood');
summaryMood.innerHTML = `
<span class="summary-mood-emoji">${moodData.emoji}</span>
<div>
<div class="summary-mood-text">${moodData.label}</div>
<div class="summary-mood-sub">${moodData.sub}</div>
</div>
`;
// Rating chips
const summaryChips = $('#summary-chips');
summaryChips.innerHTML = '';
const dims = Object.keys(state.ratings);
const ratedDims = dims.filter((d) => state.ratings[d] > 0);
if (ratedDims.length === 0) {
summaryChips.innerHTML = `<p style="font-size:0.8rem;color:var(--warm-gray);">No ratings submitted.</p>`;
} else {
ratedDims.forEach((dim) => {
const score = state.ratings[dim];
const filledStars = 'โ
'.repeat(score);
const emptyStars = 'โ'.repeat(5 - score);
const chip = document.createElement('div');
chip.className = 'summary-chip';
chip.innerHTML = `
<span class="chip-label">${DIM_LABELS[dim]}</span>
<span class="chip-stars">${filledStars}<span class="empty">${emptyStars}</span></span>
`;
summaryChips.appendChild(chip);
});
}
}
// ---- Restart ----
$('#restart-btn').addEventListener('click', () => {
// Reset state
state.step = 1;
state.mood = null;
state.ratings = { food: 0, service: 0, ambiance: 0, value: 0 };
state.comment = '';
state.contact = false;
state.email = '';
// Reset UI โ emoji
$$('.emoji-btn').forEach((b) => b.classList.remove('selected'));
// Reset stars
$$('.stars').forEach((group) => {
$$('.star-btn', group).forEach((btn) => {
btn.classList.remove('selected', 'hover-active');
btn.textContent = 'โ';
});
});
// Reset fields
commentInput.value = '';
charUsed.textContent = '0';
contactCheckbox.checked = false;
emailField.classList.add('hidden');
emailInput.value = '';
// Reset error
$('#step2-error').classList.add('hidden');
// Navigate
goToStep(1);
});
// ---- Init ----
updateProgress(1);
})();<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Post-Meal Feedback Form</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700;800&family=Inter:wght@400;500;600;700&display=swap" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="page-wrapper">
<div class="card" id="feedback-card">
<!-- Progress Bar -->
<div class="progress-bar" id="progress-bar" aria-label="Form progress">
<div class="progress-track">
<div class="progress-line">
<div class="progress-fill" id="progress-fill"></div>
</div>
<div class="progress-dots">
<div class="progress-dot active" data-step="1">
<span class="dot-circle"></span>
<span class="dot-label">Mood</span>
</div>
<div class="progress-dot" data-step="2">
<span class="dot-circle"></span>
<span class="dot-label">Ratings</span>
</div>
<div class="progress-dot" data-step="3">
<span class="dot-circle"></span>
<span class="dot-label">Comments</span>
</div>
<div class="progress-dot" data-step="4">
<span class="dot-circle"></span>
<span class="dot-label">Done</span>
</div>
</div>
</div>
</div>
<!-- Step 1: Mood -->
<div class="step" id="step-1" data-step="1">
<div class="step-header">
<p class="step-eyebrow">Step 1 of 3</p>
<h1 class="step-title">How was your evening?</h1>
<p class="step-subtitle">Tap how you feel about your overall experience tonight.</p>
</div>
<div class="emoji-row" role="group" aria-label="Overall mood">
<button class="emoji-btn" data-mood="terrible" aria-label="Terrible" type="button">
<span class="emoji-icon">๐</span>
<span class="emoji-label">Terrible</span>
</button>
<button class="emoji-btn" data-mood="poor" aria-label="Poor" type="button">
<span class="emoji-icon">๐</span>
<span class="emoji-label">Poor</span>
</button>
<button class="emoji-btn" data-mood="okay" aria-label="Okay" type="button">
<span class="emoji-icon">๐</span>
<span class="emoji-label">Okay</span>
</button>
<button class="emoji-btn" data-mood="great" aria-label="Great" type="button">
<span class="emoji-icon">๐</span>
<span class="emoji-label">Great</span>
</button>
<button class="emoji-btn" data-mood="amazing" aria-label="Amazing" type="button">
<span class="emoji-icon">๐คฉ</span>
<span class="emoji-label">Amazing</span>
</button>
</div>
</div>
<!-- Step 2: Star Ratings -->
<div class="step hidden" id="step-2" data-step="2">
<div class="step-header">
<p class="step-eyebrow">Step 2 of 3</p>
<h1 class="step-title">Rate your experience</h1>
<p class="step-subtitle">How did we do across each area?</p>
</div>
<div class="rating-rows">
<div class="rating-row">
<span class="rating-label">Food Quality</span>
<div class="stars" data-dimension="food" role="group" aria-label="Food quality rating">
<button class="star-btn" data-value="1" type="button" aria-label="1 star">โ</button>
<button class="star-btn" data-value="2" type="button" aria-label="2 stars">โ</button>
<button class="star-btn" data-value="3" type="button" aria-label="3 stars">โ</button>
<button class="star-btn" data-value="4" type="button" aria-label="4 stars">โ</button>
<button class="star-btn" data-value="5" type="button" aria-label="5 stars">โ</button>
</div>
</div>
<div class="rating-row">
<span class="rating-label">Service</span>
<div class="stars" data-dimension="service" role="group" aria-label="Service rating">
<button class="star-btn" data-value="1" type="button" aria-label="1 star">โ</button>
<button class="star-btn" data-value="2" type="button" aria-label="2 stars">โ</button>
<button class="star-btn" data-value="3" type="button" aria-label="3 stars">โ</button>
<button class="star-btn" data-value="4" type="button" aria-label="4 stars">โ</button>
<button class="star-btn" data-value="5" type="button" aria-label="5 stars">โ</button>
</div>
</div>
<div class="rating-row">
<span class="rating-label">Ambiance</span>
<div class="stars" data-dimension="ambiance" role="group" aria-label="Ambiance rating">
<button class="star-btn" data-value="1" type="button" aria-label="1 star">โ</button>
<button class="star-btn" data-value="2" type="button" aria-label="2 stars">โ</button>
<button class="star-btn" data-value="3" type="button" aria-label="3 stars">โ</button>
<button class="star-btn" data-value="4" type="button" aria-label="4 stars">โ</button>
<button class="star-btn" data-value="5" type="button" aria-label="5 stars">โ</button>
</div>
</div>
<div class="rating-row">
<span class="rating-label">Value</span>
<div class="stars" data-dimension="value" role="group" aria-label="Value rating">
<button class="star-btn" data-value="1" type="button" aria-label="1 star">โ</button>
<button class="star-btn" data-value="2" type="button" aria-label="2 stars">โ</button>
<button class="star-btn" data-value="3" type="button" aria-label="3 stars">โ</button>
<button class="star-btn" data-value="4" type="button" aria-label="4 stars">โ</button>
<button class="star-btn" data-value="5" type="button" aria-label="5 stars">โ</button>
</div>
</div>
</div>
<p class="validation-msg hidden" id="step2-error">Please rate at least one area to continue.</p>
<div class="btn-row">
<button class="btn btn-ghost" id="back-2" type="button">Back</button>
<button class="btn btn-primary" id="next-2" type="button">Next</button>
</div>
</div>
<!-- Step 3: Comments -->
<div class="step hidden" id="step-3" data-step="3">
<div class="step-header">
<p class="step-eyebrow">Step 3 of 3</p>
<h1 class="step-title">Anything else?</h1>
<p class="step-subtitle">Your feedback helps us improve every visit.</p>
</div>
<div class="field-group">
<label class="field-label" for="comment-input">Any comments? <span class="field-optional">(optional)</span></label>
<textarea
id="comment-input"
class="textarea"
rows="4"
placeholder="Tell us what made your evening special, or what we could improve..."
maxlength="500"
></textarea>
<div class="char-count"><span id="char-used">0</span> / 500</div>
</div>
<div class="field-group contact-group">
<label class="checkbox-row">
<input type="checkbox" id="contact-checkbox" />
<span class="checkbox-custom"></span>
<span class="checkbox-text">May we follow up with you about your experience?</span>
</label>
<div class="email-field hidden" id="email-field">
<label class="field-label" for="email-input">Your email</label>
<input
type="email"
id="email-input"
class="input"
placeholder="[email protected]"
/>
<p class="field-hint">We'll only use this to follow up on your feedback.</p>
</div>
</div>
<div class="btn-row">
<button class="btn btn-ghost" id="back-3" type="button">Back</button>
<button class="btn btn-primary" id="submit-btn" type="button">Submit Feedback</button>
</div>
</div>
<!-- Step 4: Thank You -->
<div class="step hidden" id="step-4" data-step="4">
<div class="thankyou-content">
<div class="checkmark-circle" id="checkmark-circle">
<span class="checkmark-icon">โ</span>
</div>
<h1 class="thankyou-title">Thank You!</h1>
<p class="thankyou-subtitle">Your feedback means a lot to us. We look forward to seeing you again.</p>
<div class="summary-block">
<div class="summary-mood" id="summary-mood"></div>
<div class="summary-chips" id="summary-chips"></div>
</div>
<button class="btn btn-primary btn-full" id="restart-btn" type="button">Leave Another Review</button>
</div>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>Post-Meal Feedback Form
Multi-step form: Step 1 โ emoji mood scale (๐๐๐๐๐คฉ) for overall experience; Step 2 โ star ratings (1โ5) for Food quality, Service, Ambiance, Value; Step 3 โ optional free-text โTell us moreโ + a checkbox โMay we contact you?โ; Step 4 โ thank you screen with summary of their ratings. Progress bar across top.