Vet — Daily Schedule
A front-desk daily schedule and waiting-room queue for a veterinary clinic, grouping appointments by vet and room with time, pet, owner and reason, status badges, check-in and call-back actions, a live waiting count, and highlighted walk-in and emergency arrivals.
MCP
Code
:root {
--teal: #179c8e;
--teal-d: #0f7f73;
--teal-50: #e6f5f2;
--coral: #ff8a5c;
--ink: #173a36;
--muted: #6b827e;
--bg: #f2f8f6;
--white: #ffffff;
--line: #dcebe7;
--radius: 20px;
--radius-sm: 12px;
--shadow: 0 12px 30px -16px rgba(15, 127, 115, 0.45);
--font: 'Inter', system-ui, -apple-system, sans-serif;
}
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: var(--font);
color: var(--ink);
background: var(--bg);
-webkit-font-smoothing: antialiased;
padding: 28px 16px 48px;
}
.board {
max-width: 980px;
margin: 0 auto;
display: grid;
gap: 20px;
}
/* ---- Header ---- */
.board__head {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--radius);
box-shadow: var(--shadow);
padding: 22px 24px;
display: flex;
flex-wrap: wrap;
gap: 20px;
align-items: center;
justify-content: space-between;
}
.board__intro {
display: flex;
align-items: center;
gap: 14px;
}
.board__paw {
font-size: 1.9rem;
background: var(--teal-50);
border-radius: 16px;
width: 54px;
height: 54px;
display: grid;
place-items: center;
}
.board__head h1 {
margin: 0;
font-size: 1.5rem;
font-weight: 700;
}
.board__date {
margin: 2px 0 0;
font-size: 0.85rem;
color: var(--muted);
}
.stats {
margin: 0;
display: flex;
gap: 10px;
}
.stat {
background: var(--teal-50);
border-radius: var(--radius-sm);
padding: 10px 16px;
text-align: center;
min-width: 72px;
}
.stat dt {
font-size: 0.7rem;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--teal-d);
font-weight: 600;
}
.stat dd {
margin: 4px 0 0;
font-size: 1.4rem;
font-weight: 700;
color: var(--ink);
}
.stat--done {
background: #eef2f1;
}
.stat--done dt {
color: var(--muted);
}
/* ---- Alerts ---- */
.alerts {
display: grid;
gap: 12px;
}
.alert {
display: flex;
align-items: center;
gap: 14px;
border-radius: var(--radius);
padding: 14px 18px;
background: var(--white);
border: 1px solid var(--line);
}
.alert--emergency {
background: #fff1ea;
border-color: #ffd2bf;
}
.alert--walkin {
background: var(--teal-50);
border-color: #c5e6df;
}
.alert__tag {
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
padding: 5px 10px;
border-radius: 999px;
color: var(--white);
white-space: nowrap;
}
.alert--emergency .alert__tag {
background: var(--coral);
}
.alert--walkin .alert__tag {
background: var(--teal);
}
.alert__body {
flex: 1;
font-size: 0.95rem;
font-weight: 600;
display: grid;
gap: 2px;
}
.alert__meta {
font-size: 0.8rem;
font-weight: 400;
color: var(--muted);
}
/* ---- Columns ---- */
.columns {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 18px;
}
.vet {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--radius);
box-shadow: var(--shadow);
padding: 18px;
display: grid;
gap: 14px;
align-content: start;
}
.vet__head {
display: flex;
align-items: center;
gap: 12px;
}
.vet__avatar {
width: 44px;
height: 44px;
border-radius: 14px;
display: grid;
place-items: center;
font-weight: 700;
font-size: 0.9rem;
color: var(--white);
background: linear-gradient(135deg, var(--teal), var(--teal-d));
}
.vet__avatar--alt {
background: linear-gradient(135deg, var(--coral), #f06b3f);
}
.vet__head h2 {
margin: 0;
font-size: 1.05rem;
}
.vet__room {
margin: 2px 0 0;
font-size: 0.78rem;
color: var(--muted);
}
/* ---- Appointments ---- */
.appts {
list-style: none;
margin: 0;
padding: 0;
display: grid;
gap: 10px;
}
.appt {
display: grid;
grid-template-columns: auto 1fr auto;
grid-template-areas:
"time info badge"
"time info actions";
gap: 4px 12px;
align-items: start;
padding: 12px 14px;
border: 1px solid var(--line);
border-radius: var(--radius-sm);
background: #fbfdfc;
transition: border-color 0.18s ease, background 0.18s ease, transform 0.18s ease;
}
.appt:hover {
border-color: #bfe1da;
}
.appt__time {
grid-area: time;
font-weight: 700;
font-size: 0.95rem;
color: var(--teal-d);
background: var(--teal-50);
border-radius: 10px;
padding: 8px 10px;
align-self: center;
min-width: 48px;
text-align: center;
}
.appt__info {
grid-area: info;
display: grid;
gap: 2px;
}
.appt__pet {
margin: 0;
font-weight: 600;
font-size: 0.95rem;
}
.appt__breed {
font-weight: 400;
color: var(--muted);
}
.appt__owner,
.appt__reason {
margin: 0;
font-size: 0.8rem;
color: var(--muted);
}
.appt__room {
margin-top: 3px;
font-size: 0.72rem;
font-weight: 600;
color: var(--teal-d);
}
.badge {
grid-area: badge;
justify-self: end;
font-size: 0.72rem;
font-weight: 700;
padding: 5px 10px;
border-radius: 999px;
white-space: nowrap;
}
.appt[data-status="booked"] .badge {
background: #eef2f1;
color: var(--muted);
}
.appt[data-status="checked-in"] .badge {
background: #fff1ea;
color: #d35a2e;
}
.appt[data-status="in-room"] .badge {
background: var(--teal-50);
color: var(--teal-d);
}
.appt[data-status="done"] {
background: #f4f8f7;
opacity: 0.82;
}
.appt[data-status="done"] .appt__pet {
text-decoration: line-through;
text-decoration-color: var(--muted);
}
.appt[data-status="done"] .badge {
background: #dcefe9;
color: var(--teal-d);
}
.appt__actions {
grid-area: actions;
justify-self: end;
align-self: end;
display: flex;
gap: 8px;
flex-wrap: wrap;
}
/* ---- Buttons ---- */
.btn {
font-family: inherit;
font-size: 0.8rem;
font-weight: 600;
border: none;
border-radius: 10px;
padding: 7px 14px;
cursor: pointer;
transition: transform 0.12s ease, background 0.18s ease, box-shadow 0.18s ease;
}
.btn:active {
transform: scale(0.96);
}
.btn:focus-visible {
outline: 3px solid rgba(23, 156, 142, 0.4);
outline-offset: 2px;
}
.btn--teal {
background: var(--teal);
color: var(--white);
}
.btn--teal:hover {
background: var(--teal-d);
}
.btn--coral {
background: var(--coral);
color: var(--white);
}
.btn--coral:hover {
background: #f06b3f;
}
.btn--ghost {
background: var(--white);
color: var(--teal-d);
border: 1px solid var(--line);
}
.btn--ghost:hover {
background: var(--teal-50);
}
.live {
margin: 0;
text-align: center;
font-size: 0.82rem;
color: var(--muted);
min-height: 1.1em;
}
/* ---- Responsive ---- */
@media (max-width: 520px) {
body {
padding: 18px 12px 36px;
}
.board__head {
padding: 18px;
}
.stats {
flex-wrap: wrap;
width: 100%;
}
.stat {
flex: 1 1 calc(50% - 5px);
}
.columns {
grid-template-columns: 1fr;
}
.alert {
flex-wrap: wrap;
}
.alert .btn {
width: 100%;
}
.appt {
grid-template-columns: auto 1fr;
grid-template-areas:
"time info"
"badge badge"
"actions actions";
}
.badge {
justify-self: start;
}
.appt__actions {
justify-self: stretch;
}
.appt__actions .btn {
flex: 1;
}
}(function () {
'use strict';
var BADGE = {
booked: 'Booked',
'checked-in': 'Checked in',
'in-room': 'In room',
done: 'Done'
};
var live = document.getElementById('live');
var roomCounter = 1;
function announce(msg) {
if (live) live.textContent = msg;
}
// Render the action buttons + badge for an appointment based on its status.
function render(appt) {
var status = appt.getAttribute('data-status');
var badge = appt.querySelector('[data-badge]');
var actions = appt.querySelector('[data-actions]');
var roomEl = appt.querySelector('[data-room]');
var petName = (appt.querySelector('.appt__pet') || {}).childNodes;
var name = petName && petName[0] ? petName[0].textContent.trim() : 'Patient';
if (badge) badge.textContent = BADGE[status] || status;
if (actions) actions.innerHTML = '';
// Room label only when the pet is in a room.
if (roomEl) {
var roomNo = appt.getAttribute('data-room-no');
if (status === 'in-room' && roomNo) {
roomEl.textContent = 'Room ' + roomNo;
roomEl.hidden = false;
} else {
roomEl.hidden = true;
}
}
if (!actions) return;
if (status === 'booked') {
addButton(actions, 'Check in', 'btn--teal', function () {
appt.setAttribute('data-status', 'checked-in');
render(appt);
update();
announce(name + ' checked in.');
});
} else if (status === 'checked-in') {
addButton(actions, 'Call back', 'btn--teal', function () {
appt.setAttribute('data-status', 'in-room');
appt.setAttribute('data-room-no', String(roomCounter));
roomCounter = roomCounter >= 4 ? 1 : roomCounter + 1;
render(appt);
update();
announce(name + ' called back to room ' + appt.getAttribute('data-room-no') + '.');
});
} else if (status === 'in-room') {
addButton(actions, 'Mark done', 'btn--ghost', function () {
appt.setAttribute('data-status', 'done');
appt.removeAttribute('data-room-no');
render(appt);
update();
announce(name + "'s visit complete.");
});
}
// done: no actions
}
function addButton(parent, label, variant, handler) {
var b = document.createElement('button');
b.type = 'button';
b.className = 'btn ' + variant;
b.textContent = label;
b.addEventListener('click', handler);
parent.appendChild(b);
}
// Recompute the header counters from the live DOM.
function update() {
var appts = document.querySelectorAll('.appt');
var c = { booked: 0, 'checked-in': 0, 'in-room': 0, done: 0 };
appts.forEach(function (a) {
var s = a.getAttribute('data-status');
if (c[s] != null) c[s]++;
});
setText('stat-waiting', c['checked-in']);
setText('stat-inroom', c['in-room']);
setText('stat-booked', c.booked);
setText('stat-done', c.done);
}
function setText(id, val) {
var el = document.getElementById(id);
if (el) el.textContent = val;
}
// Emergency / walk-in strip buttons: fade out once handled.
document.querySelectorAll('[data-emergency]').forEach(function (btn) {
btn.addEventListener('click', function () {
var card = btn.closest('.alert');
var pet = card ? (card.querySelector('strong') || {}).textContent : '';
btn.textContent = 'Queued ✓';
btn.disabled = true;
btn.style.opacity = '0.7';
announce((pet || 'Patient') + ' added to the queue.');
});
});
// Initial render of every appointment.
document.querySelectorAll('.appt').forEach(render);
update();
})();<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vet — Daily Schedule</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&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main class="board" aria-labelledby="board-title">
<header class="board__head">
<div class="board__intro">
<span class="board__paw" aria-hidden="true">🐾</span>
<div>
<h1 id="board-title">Daily Schedule</h1>
<p class="board__date">Monday, June 22 · Maple Street Animal Hospital</p>
</div>
</div>
<dl class="stats" aria-label="Schedule summary">
<div class="stat">
<dt>Waiting</dt>
<dd id="stat-waiting">0</dd>
</div>
<div class="stat">
<dt>In room</dt>
<dd id="stat-inroom">0</dd>
</div>
<div class="stat">
<dt>Booked</dt>
<dd id="stat-booked">0</dd>
</div>
<div class="stat stat--done">
<dt>Done</dt>
<dd id="stat-done">0</dd>
</div>
</dl>
</header>
<section class="alerts" aria-label="Walk-ins and emergencies">
<article class="alert alert--emergency">
<span class="alert__tag">Emergency</span>
<div class="alert__body">
<strong>Bruno</strong> · German Shepherd
<span class="alert__meta">Hit by car · owner Daniel Okafor · arrived 9:12 AM</span>
</div>
<button class="btn btn--coral" type="button" data-emergency>Triage now</button>
</article>
<article class="alert alert--walkin">
<span class="alert__tag">Walk-in</span>
<div class="alert__body">
<strong>Mochi</strong> · Domestic Shorthair
<span class="alert__meta">Vomiting · owner Priya Nair · arrived 9:34 AM</span>
</div>
<button class="btn btn--ghost" type="button" data-emergency>Add to queue</button>
</article>
</section>
<div class="columns" id="columns">
<section class="vet" aria-labelledby="vet-okoye">
<header class="vet__head">
<span class="vet__avatar" aria-hidden="true">RO</span>
<div>
<h2 id="vet-okoye">Dr. Rosa Okoye</h2>
<p class="vet__room">Exam rooms 1–2 · General</p>
</div>
</header>
<ul class="appts" id="appts-okoye">
<li class="appt" data-status="booked">
<span class="appt__time">9:00</span>
<div class="appt__info">
<p class="appt__pet">Luna <span class="appt__breed">· Beagle</span></p>
<p class="appt__owner">Owner: Marcus Bell</p>
<p class="appt__reason">Annual wellness & vaccines</p>
<span class="appt__room" data-room hidden>Room —</span>
</div>
<span class="badge" data-badge>Booked</span>
<div class="appt__actions" data-actions></div>
</li>
<li class="appt" data-status="checked-in">
<span class="appt__time">9:20</span>
<div class="appt__info">
<p class="appt__pet">Pepper <span class="appt__breed">· Cockatiel</span></p>
<p class="appt__owner">Owner: Helen Vasquez</p>
<p class="appt__reason">Beak trim & check</p>
<span class="appt__room" data-room hidden>Room —</span>
</div>
<span class="badge" data-badge>Booked</span>
<div class="appt__actions" data-actions></div>
</li>
<li class="appt" data-status="in-room" data-room-no="1">
<span class="appt__time">9:40</span>
<div class="appt__info">
<p class="appt__pet">Biscuit <span class="appt__breed">· Golden Retriever</span></p>
<p class="appt__owner">Owner: Tara Nguyen</p>
<p class="appt__reason">Limping, left foreleg</p>
<span class="appt__room" data-room hidden>Room —</span>
</div>
<span class="badge" data-badge>Booked</span>
<div class="appt__actions" data-actions></div>
</li>
<li class="appt" data-status="done">
<span class="appt__time">8:40</span>
<div class="appt__info">
<p class="appt__pet">Sir Whiskers <span class="appt__breed">· Maine Coon</span></p>
<p class="appt__owner">Owner: Leah Castillo</p>
<p class="appt__reason">Dental cleaning follow-up</p>
<span class="appt__room" data-room hidden>Room —</span>
</div>
<span class="badge" data-badge>Booked</span>
<div class="appt__actions" data-actions></div>
</li>
</ul>
</section>
<section class="vet" aria-labelledby="vet-mensah">
<header class="vet__head">
<span class="vet__avatar vet__avatar--alt" aria-hidden="true">KM</span>
<div>
<h2 id="vet-mensah">Dr. Kofi Mensah</h2>
<p class="vet__room">Exam rooms 3–4 · Surgery</p>
</div>
</header>
<ul class="appts" id="appts-mensah">
<li class="appt" data-status="checked-in">
<span class="appt__time">9:00</span>
<div class="appt__info">
<p class="appt__pet">Daisy <span class="appt__breed">· French Bulldog</span></p>
<p class="appt__owner">Owner: Sofia Romano</p>
<p class="appt__reason">Pre-op spay consult</p>
<span class="appt__room" data-room hidden>Room —</span>
</div>
<span class="badge" data-badge>Booked</span>
<div class="appt__actions" data-actions></div>
</li>
<li class="appt" data-status="booked">
<span class="appt__time">9:30</span>
<div class="appt__info">
<p class="appt__pet">Tank <span class="appt__breed">· Bullmastiff</span></p>
<p class="appt__owner">Owner: Andre Fontaine</p>
<p class="appt__reason">Suture removal</p>
<span class="appt__room" data-room hidden>Room —</span>
</div>
<span class="badge" data-badge>Booked</span>
<div class="appt__actions" data-actions></div>
</li>
<li class="appt" data-status="booked">
<span class="appt__time">10:00</span>
<div class="appt__info">
<p class="appt__pet">Olive <span class="appt__breed">· Holland Lop</span></p>
<p class="appt__owner">Owner: Yuki Tanaka</p>
<p class="appt__reason">Spay surgery</p>
<span class="appt__room" data-room hidden>Room —</span>
</div>
<span class="badge" data-badge>Booked</span>
<div class="appt__actions" data-actions></div>
</li>
<li class="appt" data-status="in-room" data-room-no="3">
<span class="appt__time">8:30</span>
<div class="appt__info">
<p class="appt__pet">Rex <span class="appt__breed">· Belgian Malinois</span></p>
<p class="appt__owner">Owner: Grace Adeyemi</p>
<p class="appt__reason">Mass removal, post-op</p>
<span class="appt__room" data-room hidden>Room —</span>
</div>
<span class="badge" data-badge>Booked</span>
<div class="appt__actions" data-actions></div>
</li>
</ul>
</section>
</div>
<p class="live" id="live" role="status" aria-live="polite"></p>
</main>
<script src="script.js"></script>
</body>
</html>Daily Schedule
A warm, front-desk control panel for a veterinary clinic’s day. The header tracks the live waiting-room count, total appointments and how many are still booked, so the desk always knows the room load at a glance. Walk-ins and emergencies surface in a highlighted strip at the top.
Each vet has a column of timed appointments showing the pet, owner, reason and an exam room. Every card carries a status badge — Booked, Checked in, In room or Done — and the matching action: check a patient in, call them back to a room, or mark a visit complete. Counts update live as the day moves along.
All interactions are vanilla JavaScript with no libraries — accessible buttons, real labels and a responsive layout that stacks the vet columns on small screens.
Illustrative UI only — not for real veterinary use.