Páginas Fácil
Hotel Booking — Confirmation
Full-page post-booking confirmation screen with a generated reference number, stay summary, what's-next checklist, hotel address, and action CTAs. Interactive copy-to-clipboard, add-to-calendar toggle, and expandable cancellation policy.
Abrir en Lab
MCP
html css vanilla-js
Targets: JS HTML
Código
/* ── Design tokens ── */
:root {
--navy: #1a2b4a;
--navy-d: #0f1d36;
--navy-2: #2d4570;
--cream: #f7f3eb;
--cream-2: #ece5d4;
--bone: #fbf8f2;
--gold: #c9a649;
--gold-light: #e0c378;
--gold-d: #a88a2e;
--ink: #161e2c;
--ink-2: #2e3a52;
--warm-gray: #6c7280;
--line: rgba(22, 30, 44, 0.1);
--line-strong: rgba(22, 30, 44, 0.18);
--success: #4a7752;
--danger: #b34232;
--warning: #d99020;
--info: #4a6da0;
--font-display: "Cormorant Garamond", Georgia, serif;
--font-body: "Inter", system-ui, sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, monospace;
--r-sm: 6px;
--r-md: 10px;
--r-lg: 16px;
--shadow-1: 0 1px 2px rgba(22, 30, 44, 0.06), 0 2px 8px rgba(22, 30, 44, 0.06);
--shadow-2: 0 12px 36px rgba(15, 29, 54, 0.16);
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: var(--font-body);
background: var(--cream);
color: var(--ink);
-webkit-font-smoothing: antialiased;
}
/* ── Topbar ── */
.topbar {
position: sticky;
top: 0;
z-index: 50;
background: var(--navy-d);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 32px;
height: 56px;
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.brand {
display: flex;
align-items: center;
gap: 10px;
text-decoration: none;
color: var(--bone);
}
.brand-mark {
width: 32px;
height: 32px;
border-radius: 50%;
background: var(--gold);
color: var(--navy-d);
font-family: var(--font-display);
font-size: 1.1rem;
font-weight: 700;
display: grid;
place-items: center;
flex-shrink: 0;
}
.brand-name {
font-family: var(--font-display);
font-size: 1.15rem;
font-weight: 600;
letter-spacing: 0.02em;
}
.brand-name em {
font-style: normal;
color: var(--gold-light);
}
.topnav {
display: flex;
gap: 24px;
}
.topnav a {
font-size: 0.86rem;
font-weight: 500;
color: rgba(251, 248, 242, 0.7);
text-decoration: none;
}
.topnav a:hover {
color: var(--bone);
}
/* ── Page ── */
.page {
max-width: 1000px;
margin: 0 auto;
padding: 40px 24px 60px;
}
/* ── Hero ── */
.hero {
text-align: center;
padding: 40px 20px 36px;
background: linear-gradient(160deg, var(--navy) 0%, var(--navy-d) 100%);
border-radius: var(--r-lg);
margin-bottom: 32px;
position: relative;
overflow: hidden;
}
.hero::before {
content: "";
position: absolute;
inset: 0;
background: radial-gradient(ellipse at 50% 0%, rgba(201, 166, 73, 0.22), transparent 65%);
pointer-events: none;
}
.hero-icon {
width: 64px;
height: 64px;
background: var(--success);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 20px;
font-size: 1.8rem;
color: #fff;
font-weight: 700;
box-shadow: 0 0 0 8px rgba(74, 119, 82, 0.18);
}
.hero h1 {
font-family: var(--font-display);
font-size: 2.8rem;
font-weight: 700;
color: var(--bone);
letter-spacing: -0.01em;
margin-bottom: 8px;
}
.hero-sub {
font-size: 1rem;
color: rgba(251, 248, 242, 0.75);
margin-bottom: 24px;
}
.ref-row {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
margin-bottom: 16px;
}
.ref-label {
font-size: 0.7rem;
text-transform: uppercase;
letter-spacing: 0.16em;
font-weight: 700;
color: var(--gold-light);
}
.ref-box {
display: flex;
align-items: center;
gap: 10px;
background: rgba(255, 255, 255, 0.07);
border: 1px solid rgba(201, 166, 73, 0.3);
border-radius: var(--r-md);
padding: 10px 18px;
}
.ref-code {
font-family: var(--font-mono);
font-size: 1.35rem;
font-weight: 700;
letter-spacing: 0.06em;
color: var(--gold-light);
font-variant-numeric: tabular-nums;
}
.btn-copy {
background: rgba(201, 166, 73, 0.18);
border: 1px solid rgba(201, 166, 73, 0.3);
border-radius: var(--r-sm);
color: var(--gold-light);
font-size: 1rem;
padding: 4px 10px;
cursor: pointer;
transition: background 0.15s;
}
.btn-copy:hover {
background: rgba(201, 166, 73, 0.32);
}
.btn-copy.copied {
background: rgba(74, 119, 82, 0.3);
color: #9ecfa8;
border-color: rgba(74, 119, 82, 0.4);
}
.hero-note {
font-size: 0.84rem;
color: rgba(251, 248, 242, 0.6);
}
.hero-note strong {
color: rgba(251, 248, 242, 0.85);
}
/* ── Content grid ── */
.content-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
align-items: start;
margin-bottom: 28px;
}
/* ── Card ── */
.card {
background: var(--bone);
border-radius: var(--r-lg);
border: 1px solid var(--line-strong);
overflow: hidden;
box-shadow: var(--shadow-1);
}
.card-title {
font-size: 0.72rem;
text-transform: uppercase;
letter-spacing: 0.14em;
font-weight: 700;
color: var(--gold-d);
padding: 16px 20px 12px;
border-bottom: 1px solid var(--line);
}
/* ── Stay card ── */
.stay-hero-img {
height: 130px;
background: linear-gradient(135deg, var(--navy-2), var(--navy-d));
position: relative;
overflow: hidden;
}
.stay-hero-img::after {
content: "302 · Deluxe Double";
position: absolute;
bottom: 10px;
left: 14px;
font-family: var(--font-mono);
font-size: 0.72rem;
color: rgba(251, 248, 242, 0.6);
font-weight: 600;
}
.stay-rows {
padding: 4px 0;
}
.stay-row {
display: flex;
justify-content: space-between;
align-items: baseline;
padding: 9px 20px;
border-bottom: 1px solid var(--line);
gap: 12px;
}
.stay-row:last-child {
border-bottom: none;
}
.s-label {
font-size: 0.8rem;
color: var(--warm-gray);
font-weight: 500;
flex-shrink: 0;
}
.s-val {
font-size: 0.88rem;
font-weight: 500;
color: var(--ink);
text-align: right;
}
.s-val.mono {
font-family: var(--font-mono);
font-size: 0.82rem;
font-variant-numeric: tabular-nums;
}
.s-val.strong {
font-weight: 700;
color: var(--navy-d);
font-size: 1rem;
}
.stay-row--total {
background: var(--cream);
}
/* ── Right column ── */
.right-col {
display: flex;
flex-direction: column;
gap: 16px;
}
/* ── Checklist ── */
.checklist {
list-style: none;
padding: 8px 0;
}
.check-item {
display: flex;
gap: 12px;
align-items: flex-start;
padding: 12px 20px;
border-bottom: 1px solid var(--line);
}
.check-item:last-child {
border-bottom: none;
}
.ci-icon {
width: 24px;
height: 24px;
border-radius: 50%;
background: var(--success);
color: #fff;
font-size: 0.8rem;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
margin-top: 1px;
}
.ci-icon.ci-pending {
background: var(--cream-2);
color: var(--warm-gray);
border: 2px solid var(--line-strong);
}
.ci-body {
flex: 1;
min-width: 0;
}
.ci-body strong {
font-size: 0.9rem;
font-weight: 600;
color: var(--ink);
display: block;
margin-bottom: 2px;
}
.ci-body p {
font-size: 0.8rem;
color: var(--warm-gray);
line-height: 1.4;
}
.btn-toggle-cal {
font-family: inherit;
font-size: 0.78rem;
font-weight: 600;
padding: 5px 14px;
background: var(--navy);
color: var(--bone);
border: none;
border-radius: 999px;
cursor: pointer;
flex-shrink: 0;
margin-top: 2px;
}
.btn-toggle-cal:hover {
background: var(--navy-2);
}
.btn-toggle-cal.is-open {
background: var(--cream-2);
color: var(--ink-2);
}
.cal-actions {
margin-top: 8px;
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.btn-cal {
font-family: inherit;
font-size: 0.78rem;
font-weight: 600;
padding: 6px 12px;
background: var(--cream);
border: 1px solid var(--line-strong);
border-radius: 999px;
cursor: pointer;
color: var(--ink-2);
}
.btn-cal:hover {
background: var(--cream-2);
border-color: var(--navy-2);
color: var(--navy-d);
}
/* ── Address card ── */
.map-placeholder {
height: 120px;
background: radial-gradient(circle at 35% 45%, rgba(201, 166, 73, 0.18), transparent 40%),
linear-gradient(160deg, #c8d4e8, #a8bcda);
display: flex;
align-items: center;
justify-content: center;
}
.map-pin {
text-align: center;
font-size: 1.6rem;
line-height: 1;
color: var(--navy-d);
filter: drop-shadow(0 2px 4px rgba(15, 29, 54, 0.2));
}
.map-pin span {
font-family: var(--font-mono);
font-size: 0.6rem;
font-weight: 700;
letter-spacing: 0.06em;
color: var(--navy-2);
text-transform: uppercase;
}
address {
font-style: normal;
font-size: 0.88rem;
line-height: 1.7;
color: var(--ink-2);
padding: 14px 20px;
}
address strong {
font-weight: 700;
color: var(--ink);
display: block;
margin-bottom: 2px;
}
address a {
color: var(--navy-2);
font-weight: 600;
text-decoration: none;
}
address a:hover {
text-decoration: underline;
}
/* ── Policy ── */
.policy-preview {
font-size: 0.86rem;
color: var(--ink-2);
padding: 12px 20px 4px;
}
.btn-policy {
display: flex;
align-items: center;
gap: 6px;
background: none;
border: none;
font-family: inherit;
font-size: 0.82rem;
font-weight: 600;
color: var(--navy-2);
padding: 8px 20px 14px;
cursor: pointer;
text-align: left;
}
.btn-policy:hover {
color: var(--navy-d);
}
.policy-detail {
background: var(--cream);
border-top: 1px solid var(--line);
padding: 14px 20px 16px;
font-size: 0.84rem;
color: var(--ink-2);
line-height: 1.6;
}
.policy-detail p {
margin-bottom: 10px;
}
.policy-detail ul {
padding-left: 18px;
display: flex;
flex-direction: column;
gap: 6px;
}
/* ── CTA bar ── */
.cta-bar {
display: flex;
justify-content: center;
gap: 12px;
flex-wrap: wrap;
}
.btn-manage,
.btn-primary {
font-family: inherit;
font-size: 0.9rem;
font-weight: 600;
padding: 13px 28px;
border-radius: 999px;
cursor: pointer;
}
.btn-manage {
background: transparent;
border: 1px solid var(--line-strong);
color: var(--ink-2);
}
.btn-manage:hover {
background: var(--bone);
border-color: var(--navy-2);
color: var(--navy-d);
}
.btn-primary {
background: var(--navy);
color: var(--bone);
border: none;
}
.btn-primary:hover {
background: var(--navy-d);
}
/* ── Toast ── */
.toast {
position: fixed;
bottom: 24px;
left: 50%;
transform: translateX(-50%);
background: var(--navy-d);
color: var(--bone);
padding: 11px 22px;
border-radius: 999px;
font-size: 0.86rem;
font-weight: 600;
box-shadow: 0 10px 30px rgba(22, 30, 44, 0.25);
z-index: 200;
white-space: nowrap;
}
/* ── Responsive ── */
@media (max-width: 960px) {
.content-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 560px) {
.page {
padding: 24px 12px 40px;
}
.hero {
padding: 28px 16px 24px;
}
.hero h1 {
font-size: 2rem;
}
.ref-code {
font-size: 1.1rem;
}
.topbar {
padding: 0 16px;
}
.topnav {
display: none;
}
}
/* ── Honor [hidden] over display (overlay/panel toggle fix) ── */
.cal-actions[hidden] {
display: none;
}// ── Toast helper ──
const toast = document.getElementById("toast");
function showToast(msg) {
toast.textContent = msg;
toast.hidden = false;
clearTimeout(showToast._t);
showToast._t = setTimeout(() => (toast.hidden = true), 2200);
}
// ── Copy reference to clipboard ──
const btnCopy = document.getElementById("btnCopy");
const copyIcon = document.getElementById("copyIcon");
const refCode = document.getElementById("refCode");
btnCopy.addEventListener("click", () => {
const text = refCode.textContent.trim();
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard
.writeText(text)
.then(() => {
showCopied();
})
.catch(() => fallbackCopy(text));
} else {
fallbackCopy(text);
}
});
function fallbackCopy(text) {
const ta = document.createElement("textarea");
ta.value = text;
ta.style.cssText = "position:fixed;opacity:0;";
document.body.appendChild(ta);
ta.select();
document.execCommand("copy");
ta.remove();
showCopied();
}
function showCopied() {
copyIcon.textContent = "✓";
btnCopy.classList.add("copied");
showToast("Reference copied: AH-2026-88341");
clearTimeout(showCopied._t);
showCopied._t = setTimeout(() => {
copyIcon.textContent = "⧉";
btnCopy.classList.remove("copied");
}, 2000);
}
// ── Add to calendar toggle ──
const btnCal = document.getElementById("btnCal");
const calActions = document.getElementById("calActions");
btnCal.addEventListener("click", () => {
const isOpen = calActions.hidden === false;
calActions.hidden = isOpen;
btnCal.textContent = isOpen ? "Add" : "Close";
btnCal.classList.toggle("is-open", !isOpen);
});
// ── Calendar provider buttons ──
document.querySelectorAll(".btn-cal").forEach((btn) => {
btn.addEventListener("click", () => {
const provider = btn.dataset.cal;
const providerName =
{ google: "Google Calendar", apple: "Apple Calendar", outlook: "Outlook" }[provider] ||
provider;
// In a real app this would build a calendar URL; here we just show feedback
showToast(`Opening ${providerName}…`);
// Mark the checklist item as done
const calItem = document.getElementById("calItem");
const icon = calItem.querySelector(".ci-icon");
icon.textContent = "✓";
icon.classList.remove("ci-pending");
calActions.hidden = true;
btnCal.textContent = "Done";
btnCal.classList.remove("is-open");
btnCal.disabled = true;
btnCal.style.opacity = "0.5";
});
});
// ── Cancellation policy accordion ──
const btnPolicy = document.getElementById("btnPolicy");
const policyDetail = document.getElementById("policyDetail");
const policyArrow = document.getElementById("policyArrow");
btnPolicy.addEventListener("click", () => {
const isOpen = policyDetail.hidden === false;
policyDetail.hidden = isOpen;
policyArrow.textContent = isOpen ? "↓" : "↑";
btnPolicy.setAttribute("aria-expanded", String(!isOpen));
});
// ── Manage booking CTA (mock) ──
document.getElementById("btnManage").addEventListener("click", () => {
showToast("Opening booking management…");
});
// ── Back to home CTA (mock) ──
document.getElementById("btnBack").addEventListener("click", () => {
showToast("Returning to Aurelia Hotels home…");
});<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Cormorant+Garamond:wght@600;700&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@500;700&display=swap"
/>
<link rel="stylesheet" href="style.css" />
<title>Booking Confirmed · Aurelia Hotels</title>
</head>
<body>
<!-- ── Top nav ── -->
<header class="topbar">
<a class="brand" href="#">
<span class="brand-mark">A</span>
<span class="brand-name">Aurelia <em>Hotels</em></span>
</a>
<nav class="topnav">
<a href="#">My bookings</a>
<a href="#">Help</a>
</nav>
</header>
<main class="page">
<!-- ── Success hero ── -->
<section class="hero">
<div class="hero-icon">✓</div>
<h1>You're booked!</h1>
<p class="hero-sub">Your reservation at Aurelia Madrid is confirmed.</p>
<div class="ref-row">
<span class="ref-label">Booking reference</span>
<div class="ref-box">
<span class="ref-code" id="refCode">AH-2026-88341</span>
<button class="btn-copy" id="btnCopy" type="button" aria-label="Copy reference">
<span id="copyIcon">⧉</span>
</button>
</div>
</div>
<p class="hero-note">A confirmation email has been sent to <strong>[email protected]</strong></p>
</section>
<!-- ── Content grid ── -->
<div class="content-grid">
<!-- ── Stay summary ── -->
<section class="card stay-card">
<h2 class="card-title">Stay summary</h2>
<div class="stay-hero-img" aria-hidden="true"></div>
<div class="stay-rows">
<div class="stay-row">
<span class="s-label">Property</span>
<span class="s-val">Aurelia Madrid, Gran Vía</span>
</div>
<div class="stay-row">
<span class="s-label">Room</span>
<span class="s-val">Deluxe Double — City View</span>
</div>
<div class="stay-row">
<span class="s-label">Check-in</span>
<span class="s-val mono">Tue, 09 Jun 2026 · from 15:00</span>
</div>
<div class="stay-row">
<span class="s-label">Check-out</span>
<span class="s-val mono">Fri, 12 Jun 2026 · by 12:00</span>
</div>
<div class="stay-row">
<span class="s-label">Duration</span>
<span class="s-val">3 nights</span>
</div>
<div class="stay-row">
<span class="s-label">Guests</span>
<span class="s-val">2 adults</span>
</div>
<div class="stay-row">
<span class="s-label">Rate plan</span>
<span class="s-val">Best Available Rate (non-refundable)</span>
</div>
<div class="stay-row stay-row--total">
<span class="s-label">Total charged</span>
<span class="s-val mono strong">€552.00</span>
</div>
</div>
</section>
<!-- ── Right column ── -->
<div class="right-col">
<!-- ── What's next checklist ── -->
<section class="card">
<h2 class="card-title">What's next</h2>
<ul class="checklist">
<li class="check-item is-done">
<span class="ci-icon">✓</span>
<div class="ci-body">
<strong>Confirmation email sent</strong>
<p>Check your inbox at [email protected]</p>
</div>
</li>
<li class="check-item" id="calItem">
<span class="ci-icon ci-pending">○</span>
<div class="ci-body">
<strong>Add to calendar</strong>
<p>Save the dates to your calendar</p>
<div class="cal-actions" id="calActions" hidden>
<button class="btn-cal" type="button" data-cal="google">Google Calendar</button>
<button class="btn-cal" type="button" data-cal="apple">Apple Calendar</button>
<button class="btn-cal" type="button" data-cal="outlook">Outlook</button>
</div>
</div>
<button class="btn-toggle-cal" id="btnCal" type="button">Add</button>
</li>
<li class="check-item">
<span class="ci-icon ci-pending">○</span>
<div class="ci-body">
<strong>Online check-in opens 24 h before arrival</strong>
<p>You'll receive a reminder on 8 Jun 2026</p>
</div>
</li>
<li class="check-item">
<span class="ci-icon ci-pending">○</span>
<div class="ci-body">
<strong>Your room is guaranteed from 15:00</strong>
<p>Early arrivals subject to availability</p>
</div>
</li>
</ul>
</section>
<!-- ── Hotel address + map placeholder ── -->
<section class="card address-card">
<h2 class="card-title">Hotel address</h2>
<div class="map-placeholder" aria-label="Map placeholder">
<div class="map-pin">▲<br/><span>Aurelia</span></div>
</div>
<address class="addr-block">
<strong>Aurelia Madrid</strong><br />
Gran Vía 28, 4ª planta<br />
28013 Madrid, Spain<br />
<a href="tel:+34912345678">+34 91 234 56 78</a>
</address>
</section>
<!-- ── Cancellation policy ── -->
<section class="card">
<h2 class="card-title">Cancellation policy</h2>
<div class="policy-preview">Non-refundable rate — charges apply upon cancellation.</div>
<button class="btn-policy" id="btnPolicy" type="button" aria-expanded="false">
View full policy <span id="policyArrow">↓</span>
</button>
<div class="policy-detail" id="policyDetail" hidden>
<p>This reservation is <strong>non-refundable</strong>. Full cancellation fees apply from the moment of booking.</p>
<ul>
<li>100% of total stay charged on cancellation at any time.</li>
<li>No-show is treated as a cancellation — full charge applies.</li>
<li>Date modifications subject to availability and rate difference.</li>
<li>In exceptional circumstances contact reception 72 h in advance.</li>
</ul>
</div>
</section>
</div>
</div>
<!-- ── CTA bar ── -->
<div class="cta-bar">
<button class="btn-manage" type="button" id="btnManage">Manage booking</button>
<button class="btn-primary" type="button" id="btnBack">Back to home</button>
</div>
</main>
<div class="toast" id="toast" hidden role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Booking Confirmation
The page a guest sees immediately after completing a reservation. A success hero displays the auto-generated booking reference with a copy-to-clipboard button (toast feedback), followed by a clean stay summary card, a “what’s next” action checklist, a hotel address block with a CSS map placeholder, and CTAs to manage the booking or add to calendar. An expandable accordion reveals the full cancellation policy without cluttering the page.