Dental — Appointment Card
A trust-first dental appointment card with date and time, assigned dentist, treatment type, a confirmed or pending status badge, and reschedule plus cancel actions that open an inline confirm step before anything changes.
MCP
Code
:root {
--blue: #2563eb;
--blue-d: #1d4ed8;
--blue-50: #eff5ff;
--mint: #34d399;
--mint-50: #e8fbf3;
--ink: #0f2740;
--ink-2: #3a5169;
--muted: #6b7c90;
--bg: #f3f8ff;
--white: #fff;
--line: rgba(15, 39, 64, 0.10);
--line-2: rgba(15, 39, 64, 0.18);
--ok: #22b07d;
--warn: #d98a2b;
--danger: #e05252;
--r-sm: 8px;
--r-md: 14px;
--r-lg: 22px;
--sh-sm: 0 1px 2px rgba(15, 39, 64, 0.06);
--sh-md: 0 10px 30px -12px rgba(15, 39, 64, 0.22);
--sh-lg: 0 24px 60px -24px rgba(37, 99, 235, 0.28);
}
* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
margin: 0;
font-family: "Inter", system-ui, -apple-system, sans-serif;
line-height: 1.5;
color: var(--ink);
background:
radial-gradient(1200px 600px at 100% -10%, var(--blue-50), transparent 60%),
radial-gradient(900px 500px at -10% 110%, var(--mint-50), transparent 55%),
var(--bg);
min-height: 100vh;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.page {
max-width: 560px;
margin: 0 auto;
padding: 40px 20px 64px;
}
/* Header */
.page__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 22px;
}
.brand { display: flex; align-items: center; gap: 12px; }
.brand__mark {
display: grid;
place-items: center;
width: 42px;
height: 42px;
border-radius: var(--r-md);
color: var(--white);
background: linear-gradient(140deg, var(--blue), var(--blue-d));
box-shadow: var(--sh-md);
}
.brand__name { margin: 0; font-weight: 800; font-size: 16px; letter-spacing: -0.01em; }
.brand__sub { margin: 0; font-size: 12.5px; color: var(--muted); font-weight: 500; }
.pill {
font-size: 12px;
font-weight: 700;
color: var(--blue-d);
background: var(--white);
border: 1px solid var(--line);
padding: 7px 12px;
border-radius: 999px;
box-shadow: var(--sh-sm);
white-space: nowrap;
}
/* Cards */
.cards { display: grid; gap: 16px; }
.card {
position: relative;
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 18px 18px 16px;
box-shadow: var(--sh-md);
transition: transform .2s ease, box-shadow .25s ease, border-color .2s ease, opacity .3s ease;
}
.card:hover {
transform: translateY(-3px);
box-shadow: var(--sh-lg);
border-color: var(--line-2);
}
.card__top { display: flex; gap: 14px; }
/* Date tile */
.date {
flex: 0 0 auto;
width: 58px;
text-align: center;
border-radius: var(--r-md);
padding: 8px 6px 10px;
background: linear-gradient(160deg, var(--blue-50), #fff);
border: 1px solid var(--line);
color: var(--ink);
align-self: flex-start;
}
.date__mon { display: block; font-size: 10.5px; font-weight: 800; letter-spacing: .08em; color: var(--blue-d); }
.date__day { display: block; font-size: 24px; font-weight: 800; line-height: 1.05; letter-spacing: -0.02em; }
.date__dow { display: block; font-size: 10.5px; font-weight: 600; color: var(--muted); }
.card__head {
flex: 1 1 auto;
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 10px;
min-width: 0;
}
.card__titles { min-width: 0; }
.card__treatment {
margin: 1px 0 6px;
font-size: 16px;
font-weight: 700;
letter-spacing: -0.01em;
color: var(--ink);
}
.card__meta {
margin: 0;
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
font-size: 12.5px;
font-weight: 500;
color: var(--ink-2);
}
.metaitem { display: inline-flex; align-items: center; gap: 5px; }
.dot { width: 3px; height: 3px; border-radius: 50%; background: var(--line-2); }
.ic {
width: 15px;
height: 15px;
fill: none;
stroke: currentColor;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
flex: 0 0 auto;
}
/* Badge */
.badge {
display: inline-flex;
align-items: center;
gap: 5px;
flex: 0 0 auto;
font-size: 11.5px;
font-weight: 700;
padding: 5px 10px 5px 8px;
border-radius: 999px;
border: 1px solid transparent;
white-space: nowrap;
}
.badge .ic { width: 13px; height: 13px; stroke-width: 2.4; }
.badge--confirmed { color: var(--ok); background: var(--mint-50); border-color: rgba(34, 176, 125, 0.25); }
.badge--pending { color: var(--warn); background: #fdf3e6; border-color: rgba(217, 138, 43, 0.28); }
.badge--cancelled { color: var(--danger); background: #fdecec; border-color: rgba(224, 82, 82, 0.28); }
/* Dentist row */
.dentist {
display: flex;
align-items: center;
gap: 11px;
margin: 14px 0 0;
padding: 11px 12px;
border-radius: var(--r-md);
background: #f7fafe;
border: 1px solid var(--line);
}
.avatar {
flex: 0 0 auto;
width: 38px;
height: 38px;
border-radius: 50%;
display: grid;
place-items: center;
color: var(--white);
font-size: 13px;
font-weight: 700;
letter-spacing: .02em;
background: linear-gradient(140deg, var(--a, var(--blue)), var(--b, var(--mint)));
box-shadow: var(--sh-sm);
}
.dentist__name { margin: 0; font-size: 14px; font-weight: 700; }
.dentist__role { margin: 0; font-size: 12px; color: var(--muted); font-weight: 500; }
.dur {
margin-left: auto;
font-size: 11.5px;
font-weight: 700;
color: var(--blue-d);
background: var(--blue-50);
border: 1px solid var(--line);
padding: 4px 9px;
border-radius: 999px;
white-space: nowrap;
}
/* Quick confirm slot (pending only) */
.confirmslot {
display: flex;
align-items: center;
justify-content: center;
gap: 7px;
width: 100%;
margin-top: 12px;
padding: 9px 12px;
font: inherit;
font-size: 12.5px;
font-weight: 700;
color: var(--ok);
background: var(--mint-50);
border: 1px dashed rgba(34, 176, 125, 0.45);
border-radius: var(--r-md);
cursor: pointer;
transition: background .18s ease, border-color .18s ease, transform .12s ease;
}
.confirmslot:hover { background: #dcf7ec; border-color: var(--ok); }
.confirmslot:active { transform: scale(.99); }
.confirmslot .ic { width: 14px; height: 14px; }
/* Actions */
.card__actions {
display: flex;
gap: 10px;
margin-top: 14px;
}
.btn {
flex: 1 1 auto;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 6px;
font: inherit;
font-size: 13px;
font-weight: 700;
padding: 10px 14px;
border-radius: var(--r-md);
border: 1px solid transparent;
cursor: pointer;
color: var(--white);
background: linear-gradient(140deg, var(--blue), var(--blue-d));
box-shadow: var(--sh-sm);
transition: transform .12s ease, box-shadow .2s ease, background .18s ease, color .18s ease, border-color .18s ease;
}
.btn:hover { box-shadow: 0 8px 18px -8px rgba(37, 99, 235, 0.55); }
.btn:active { transform: translateY(1px) scale(.99); }
.btn--ghost {
color: var(--blue-d);
background: var(--white);
border-color: var(--line-2);
box-shadow: none;
}
.btn--ghost:hover { background: var(--blue-50); box-shadow: none; }
.btn--danger-ghost {
color: var(--danger);
background: var(--white);
border-color: rgba(224, 82, 82, 0.28);
box-shadow: none;
}
.btn--danger-ghost:hover { background: #fdecec; box-shadow: none; }
.btn--soft {
color: var(--ink-2);
background: #eef3f9;
border-color: var(--line);
box-shadow: none;
}
.btn--soft:hover { background: #e4ebf3; }
.btn .ic { width: 15px; height: 15px; }
/* Focus states */
.btn:focus-visible,
.confirmslot:focus-visible {
outline: none;
box-shadow: 0 0 0 3px var(--white), 0 0 0 5px var(--blue);
}
/* Inline confirm strip */
.confirm {
margin-top: 12px;
padding: 12px;
border-radius: var(--r-md);
background: #fff8f1;
border: 1px solid rgba(217, 138, 43, 0.28);
animation: slideIn .22s ease;
}
.confirm[data-danger="1"] {
background: #fdf0f0;
border-color: rgba(224, 82, 82, 0.3);
}
.confirm__msg { margin: 0 0 10px; font-size: 13px; font-weight: 600; color: var(--ink-2); }
.confirm__actions { display: flex; gap: 8px; }
.confirm__actions .btn { flex: 1 1 auto; }
.confirm[data-danger="1"] [data-confirm-yes] {
background: linear-gradient(140deg, #ef6a6a, var(--danger));
}
@keyframes slideIn {
from { opacity: 0; transform: translateY(-6px); }
to { opacity: 1; transform: translateY(0); }
}
/* Cancelled state */
.card[data-status="cancelled"] { opacity: .62; }
.card[data-status="cancelled"] .card__actions,
.card[data-status="cancelled"] .confirmslot { display: none; }
.card[data-status="cancelled"] .card__treatment { text-decoration: line-through; text-decoration-color: var(--line-2); }
.card[data-status="cancelled"]:hover { transform: none; box-shadow: var(--sh-md); }
/* Toast */
.toast-wrap {
position: fixed;
left: 50%;
bottom: 22px;
transform: translateX(-50%);
display: flex;
flex-direction: column;
gap: 8px;
z-index: 60;
width: max-content;
max-width: calc(100vw - 32px);
pointer-events: none;
}
.toast {
display: flex;
align-items: center;
gap: 9px;
padding: 11px 15px;
border-radius: 999px;
background: var(--ink);
color: #eaf1fb;
font-size: 13px;
font-weight: 600;
box-shadow: var(--sh-lg);
animation: toastIn .28s cubic-bezier(.2, .8, .2, 1);
}
.toast.--out { animation: toastOut .3s ease forwards; }
.toast .ic { width: 16px; height: 16px; }
.toast--ok .ic { color: var(--mint); }
.toast--warn .ic { color: #f2b563; }
.toast--danger .ic { color: #f28c8c; }
@keyframes toastIn {
from { opacity: 0; transform: translateY(12px) scale(.96); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes toastOut {
to { opacity: 0; transform: translateY(10px) scale(.97); }
}
/* Responsive */
@media (max-width: 520px) {
.page { padding: 28px 14px 60px; }
.card__head { flex-direction: column; align-items: flex-start; }
.badge { order: -1; }
.card__treatment { font-size: 15px; }
.dur { display: none; }
.card__actions { flex-direction: column; }
.btn { width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
* { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}(function () {
"use strict";
var ICONS = {
ok: '<path d="M20 6 9 17l-5-5"/>',
warn: '<circle cx="12" cy="12" r="9"/><path d="M12 8v4l3 2"/>',
danger: '<path d="M18 6 6 18M6 6l12 12"/>'
};
var BADGE = {
confirmed: { cls: "badge--confirmed", label: "Confirmed", icon: '<path d="M20 6 9 17l-5-5"/>' },
pending: { cls: "badge--pending", label: "Pending", icon: '<circle cx="12" cy="12" r="9"/><path d="M12 8v4l3 2"/>' },
cancelled: { cls: "badge--cancelled", label: "Cancelled", icon: '<path d="M18 6 6 18M6 6l12 12"/>' }
};
/* Suggested reschedule slots, cycled per card */
var SLOTS = ["10:45 AM", "1:15 PM", "3:30 PM", "8:15 AM", "4:00 PM"];
var slotIx = {};
var toastWrap = document.querySelector("[data-toast-wrap]");
function toast(msg, kind) {
kind = kind || "ok";
var el = document.createElement("div");
el.className = "toast toast--" + kind;
el.setAttribute("role", kind === "danger" ? "alert" : "status");
el.innerHTML =
'<svg viewBox="0 0 24 24" class="ic" fill="none" stroke="currentColor" stroke-width="2.2" ' +
'stroke-linecap="round" stroke-linejoin="round">' + (ICONS[kind] || ICONS.ok) + "</svg>" +
"<span></span>";
el.lastChild.textContent = msg;
toastWrap.appendChild(el);
setTimeout(function () {
el.classList.add("--out");
setTimeout(function () { el.remove(); }, 320);
}, 2600);
}
function setStatus(card, status) {
card.dataset.status = status;
var badge = card.querySelector("[data-badge]");
var conf = BADGE[status];
badge.className = "badge " + conf.cls;
badge.querySelector("svg").innerHTML = conf.icon;
badge.querySelector("[data-badge-text]").textContent = conf.label;
syncPendingSlot(card, status);
}
/* Show/hide the quick-confirm strip depending on status */
function syncPendingSlot(card, status) {
var slot = card.querySelector("[data-act='accept']");
if (!slot) return;
slot.style.display = status === "pending" ? "" : "none";
}
function treatmentName(card) {
return card.querySelector(".card__treatment").textContent.trim();
}
function closeConfirm(card) {
var box = card.querySelector("[data-confirm]");
box.hidden = true;
box.removeAttribute("data-danger");
card.querySelectorAll(".card__actions .btn").forEach(function (b) { b.disabled = false; });
}
function openConfirm(card, act) {
var box = card.querySelector("[data-confirm]");
var msg = box.querySelector("[data-confirm-msg]");
box.dataset.pendingAct = act;
if (act === "cancel") {
box.setAttribute("data-danger", "1");
msg.textContent = "Cancel your " + treatmentName(card) + " appointment? This can’t be undone.";
} else {
box.removeAttribute("data-danger");
var next = nextSlot(card.dataset.appt);
box.dataset.newTime = next;
msg.textContent = "Move this visit to the next open slot at " + next + "?";
}
box.hidden = false;
card.querySelectorAll(".card__actions .btn").forEach(function (b) { b.disabled = true; });
var focusBtn = box.querySelector("[data-confirm-yes]");
if (focusBtn) focusBtn.focus();
}
function nextSlot(id) {
if (slotIx[id] === undefined) slotIx[id] = Math.floor(Math.random() * SLOTS.length);
var s = SLOTS[slotIx[id] % SLOTS.length];
slotIx[id]++;
return s;
}
function applyReschedule(card, newTime) {
var timeEl = card.querySelector("[data-field='time']");
if (timeEl) timeEl.textContent = newTime;
setStatus(card, "pending");
toast(treatmentName(card) + " moved to " + newTime + " — awaiting confirmation.", "warn");
}
function applyCancel(card) {
setStatus(card, "cancelled");
closeConfirm(card);
toast(treatmentName(card) + " appointment cancelled.", "danger");
}
/* Event delegation */
document.querySelector(".cards").addEventListener("click", function (e) {
var btn = e.target.closest("button");
if (!btn) return;
var card = btn.closest(".card");
if (!card) return;
var act = btn.dataset.act;
if (act === "reschedule" || act === "cancel") {
openConfirm(card, act);
return;
}
if (act === "accept") {
setStatus(card, "confirmed");
toast(treatmentName(card) + " is now confirmed. See you soon!", "ok");
return;
}
if (btn.hasAttribute("data-confirm-no")) {
closeConfirm(card);
return;
}
if (btn.hasAttribute("data-confirm-yes")) {
var box = card.querySelector("[data-confirm]");
var pending = box.dataset.pendingAct;
if (pending === "cancel") {
applyCancel(card);
} else {
var newTime = box.dataset.newTime;
closeConfirm(card);
applyReschedule(card, newTime);
}
}
});
/* Esc closes any open confirm strip */
document.addEventListener("keydown", function (e) {
if (e.key !== "Escape") return;
document.querySelectorAll("[data-confirm]:not([hidden])").forEach(function (box) {
closeConfirm(box.closest(".card"));
});
});
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Dental — Appointment Card</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=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main class="page">
<header class="page__head">
<div class="brand">
<span class="brand__mark" aria-hidden="true">
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 5.5c-1.6-1.6-4.2-2-6-.8-2.4 1.6-2.3 5-1.4 8 .5 1.7 1.2 4.6 2.4 5.5.9.7 1.7-.4 2-1.6.4-1.6.6-3.1 2-3.1s1.6 1.5 2 3.1c.3 1.2 1.1 2.3 2 1.6 1.2-.9 1.9-3.8 2.4-5.5.9-3 1-6.4-1.4-8-1.8-1.2-4.4-.8-6 .8Z"/>
</svg>
</span>
<div>
<p class="brand__name">Brightmile Dental</p>
<p class="brand__sub">Your upcoming visits</p>
</div>
</div>
<span class="pill">3 scheduled</span>
</header>
<section class="cards" aria-label="Upcoming dental appointments">
<!-- CONFIRMED -->
<article class="card" data-appt="a1" data-status="confirmed">
<div class="card__top">
<div class="date" aria-hidden="true">
<span class="date__mon">JUL</span>
<span class="date__day">08</span>
<span class="date__dow">Tue</span>
</div>
<div class="card__head">
<div class="card__titles">
<h2 class="card__treatment">Routine Cleaning & Check-up</h2>
<p class="card__meta">
<span class="metaitem"><svg viewBox="0 0 24 24" class="ic"><path d="M12 6v6l4 2"/><circle cx="12" cy="12" r="9"/></svg><span data-field="time">9:30 AM</span></span>
<span class="dot" aria-hidden="true"></span>
<span class="metaitem"><svg viewBox="0 0 24 24" class="ic"><path d="M3 21V9l9-6 9 6v12"/><path d="M9 21v-6h6v6"/></svg>Chair 2</span>
</p>
</div>
<span class="badge badge--confirmed" data-badge role="status">
<svg viewBox="0 0 24 24" class="ic"><path d="M20 6 9 17l-5-5"/></svg><span data-badge-text>Confirmed</span>
</span>
</div>
</div>
<div class="dentist">
<span class="avatar" style="--a:#2563eb;--b:#34d399" aria-hidden="true">SR</span>
<div>
<p class="dentist__name">Dr. Sofia Ramirez</p>
<p class="dentist__role">General Dentistry</p>
</div>
<span class="dur">45 min</span>
</div>
<div class="card__actions">
<button class="btn btn--ghost" type="button" data-act="reschedule">
<svg viewBox="0 0 24 24" class="ic"><path d="M3 12a9 9 0 1 0 3-6.7L3 8"/><path d="M3 3v5h5"/></svg>Reschedule
</button>
<button class="btn btn--danger-ghost" type="button" data-act="cancel">
<svg viewBox="0 0 24 24" class="ic"><path d="M18 6 6 18M6 6l12 12"/></svg>Cancel
</button>
</div>
<div class="confirm" data-confirm hidden>
<p class="confirm__msg" data-confirm-msg></p>
<div class="confirm__actions">
<button class="btn btn--soft" type="button" data-confirm-no>Keep it</button>
<button class="btn" type="button" data-confirm-yes>Confirm</button>
</div>
</div>
</article>
<!-- PENDING -->
<article class="card" data-appt="a2" data-status="pending">
<div class="card__top">
<div class="date" aria-hidden="true">
<span class="date__mon">JUL</span>
<span class="date__day">15</span>
<span class="date__dow">Tue</span>
</div>
<div class="card__head">
<div class="card__titles">
<h2 class="card__treatment">Teeth Whitening Session</h2>
<p class="card__meta">
<span class="metaitem"><svg viewBox="0 0 24 24" class="ic"><path d="M12 6v6l4 2"/><circle cx="12" cy="12" r="9"/></svg><span data-field="time">2:00 PM</span></span>
<span class="dot" aria-hidden="true"></span>
<span class="metaitem"><svg viewBox="0 0 24 24" class="ic"><path d="M3 21V9l9-6 9 6v12"/><path d="M9 21v-6h6v6"/></svg>Chair 4</span>
</p>
</div>
<span class="badge badge--pending" data-badge role="status">
<svg viewBox="0 0 24 24" class="ic"><circle cx="12" cy="12" r="9"/><path d="M12 8v4l3 2"/></svg><span data-badge-text>Pending</span>
</span>
</div>
</div>
<div class="dentist">
<span class="avatar" style="--a:#7c3aed;--b:#2563eb" aria-hidden="true">MC</span>
<div>
<p class="dentist__name">Dr. Marcus Chen</p>
<p class="dentist__role">Cosmetic Dentistry</p>
</div>
<span class="dur">60 min</span>
</div>
<button class="confirmslot" type="button" data-act="accept">
<svg viewBox="0 0 24 24" class="ic"><path d="M20 6 9 17l-5-5"/></svg>
Confirm this time
</button>
<div class="card__actions">
<button class="btn btn--ghost" type="button" data-act="reschedule">
<svg viewBox="0 0 24 24" class="ic"><path d="M3 12a9 9 0 1 0 3-6.7L3 8"/><path d="M3 3v5h5"/></svg>Reschedule
</button>
<button class="btn btn--danger-ghost" type="button" data-act="cancel">
<svg viewBox="0 0 24 24" class="ic"><path d="M18 6 6 18M6 6l12 12"/></svg>Cancel
</button>
</div>
<div class="confirm" data-confirm hidden>
<p class="confirm__msg" data-confirm-msg></p>
<div class="confirm__actions">
<button class="btn btn--soft" type="button" data-confirm-no>Keep it</button>
<button class="btn" type="button" data-confirm-yes>Confirm</button>
</div>
</div>
</article>
<!-- CONFIRMED / ortho -->
<article class="card" data-appt="a3" data-status="confirmed">
<div class="card__top">
<div class="date" aria-hidden="true">
<span class="date__mon">JUL</span>
<span class="date__day">22</span>
<span class="date__dow">Tue</span>
</div>
<div class="card__head">
<div class="card__titles">
<h2 class="card__treatment">Orthodontic Adjustment</h2>
<p class="card__meta">
<span class="metaitem"><svg viewBox="0 0 24 24" class="ic"><path d="M12 6v6l4 2"/><circle cx="12" cy="12" r="9"/></svg><span data-field="time">11:15 AM</span></span>
<span class="dot" aria-hidden="true"></span>
<span class="metaitem"><svg viewBox="0 0 24 24" class="ic"><path d="M3 21V9l9-6 9 6v12"/><path d="M9 21v-6h6v6"/></svg>Chair 1</span>
</p>
</div>
<span class="badge badge--confirmed" data-badge role="status">
<svg viewBox="0 0 24 24" class="ic"><path d="M20 6 9 17l-5-5"/></svg><span data-badge-text>Confirmed</span>
</span>
</div>
</div>
<div class="dentist">
<span class="avatar" style="--a:#0ea5e9;--b:#34d399" aria-hidden="true">AP</span>
<div>
<p class="dentist__name">Dr. Amara Patel</p>
<p class="dentist__role">Orthodontics</p>
</div>
<span class="dur">30 min</span>
</div>
<div class="card__actions">
<button class="btn btn--ghost" type="button" data-act="reschedule">
<svg viewBox="0 0 24 24" class="ic"><path d="M3 12a9 9 0 1 0 3-6.7L3 8"/><path d="M3 3v5h5"/></svg>Reschedule
</button>
<button class="btn btn--danger-ghost" type="button" data-act="cancel">
<svg viewBox="0 0 24 24" class="ic"><path d="M18 6 6 18M6 6l12 12"/></svg>Cancel
</button>
</div>
<div class="confirm" data-confirm hidden>
<p class="confirm__msg" data-confirm-msg></p>
<div class="confirm__actions">
<button class="btn btn--soft" type="button" data-confirm-no>Keep it</button>
<button class="btn" type="button" data-confirm-yes>Confirm</button>
</div>
</div>
</article>
</section>
</main>
<div class="toast-wrap" data-toast-wrap aria-live="polite" aria-atomic="true"></div>
<script src="script.js"></script>
</body>
</html>A clean, clinical appointment card built for a dental practice dashboard. Each card leads with a rounded date tile, then lays out the treatment, the assigned dentist with a color avatar, the visit time and chair, and a status badge that reads either confirmed or pending at a glance. The palette stays calm and reassuring — soft blues, mint accents, and generous white space — so the interface feels trustworthy rather than busy.
The two primary actions, reschedule and cancel, never fire immediately. Tapping either one slides an inline confirmation strip into the card so a mistaken click can be undone before anything happens. Confirming a cancel flips the card into a dimmed cancelled state, confirming a reschedule bumps the visit to a new suggested slot and marks it pending, and every change raises an accessible toast. Pending cards also expose a quick confirm shortcut.
Everything is keyboard reachable with visible focus rings, the status is announced through an aria-live region, and the layout collapses gracefully to a single column below 520px. No frameworks, no build step — just semantic HTML, one stylesheet, and a small vanilla script.
Illustrative UI only — not intended for real medical use.