Vet — Clinic Dashboard
A friendly veterinary clinic admin dashboard with KPI cards for appointments, patients and revenue, a CSS bar chart of appointments by day, an appointments-by-type breakdown, room and vet utilization meters, and a recent-patients table with live filtering and animated counters.
MCP
Code
:root {
--teal: #179c8e;
--teal-d: #0f7f73;
--teal-50: #e6f5f2;
--coral: #ff8a5c;
--ink: #173a36;
--muted: #6b827e;
--bg: #f2f8f6;
--white: #ffffff;
--line: #e1ece9;
--green: #1f9d6b;
--amber: #e0922f;
--blue: #3b82c4;
--radius: 18px;
--radius-sm: 12px;
--shadow: 0 10px 30px -18px rgba(15, 127, 115, 0.45);
--font: 'Inter', system-ui, -apple-system, sans-serif;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: var(--font);
background:
radial-gradient(900px 400px at 100% -10%, rgba(255, 138, 92, 0.10), transparent 60%),
var(--bg);
color: var(--ink);
line-height: 1.45;
-webkit-font-smoothing: antialiased;
min-height: 100vh;
}
.sr-only {
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.app { max-width: 1120px; margin: 0 auto; padding: 22px clamp(14px, 3vw, 28px) 48px; }
/* Topbar */
.topbar {
display: flex; align-items: center; justify-content: space-between;
gap: 16px; flex-wrap: wrap; margin-bottom: 26px;
}
.brand { display: flex; align-items: center; gap: 13px; }
.brand__logo {
width: 44px; height: 44px; border-radius: 14px;
display: grid; place-items: center;
background: linear-gradient(135deg, var(--teal), var(--teal-d));
color: #fff; box-shadow: var(--shadow);
}
.brand__name { font-size: 1.06rem; font-weight: 800; letter-spacing: -0.01em; }
.brand__sub { font-size: 0.8rem; color: var(--muted); }
.topbar__right { display: flex; align-items: center; gap: 12px; }
.search {
display: flex; align-items: center; gap: 8px;
background: var(--white); border: 1px solid var(--line);
border-radius: 999px; padding: 9px 15px; color: var(--muted);
transition: border-color .18s, box-shadow .18s;
}
.search:focus-within { border-color: var(--teal); box-shadow: 0 0 0 4px var(--teal-50); }
.search input {
border: 0; outline: 0; background: transparent; font: inherit;
color: var(--ink); width: 210px;
}
.search input::placeholder { color: var(--muted); }
.avatar {
width: 42px; height: 42px; border-radius: 50%; flex: none;
display: grid; place-items: center; font-weight: 700; font-size: 0.85rem;
color: #fff; background: linear-gradient(135deg, var(--coral), #f56b3e);
box-shadow: var(--shadow);
}
/* Page head */
.page-head {
display: flex; align-items: flex-start; justify-content: space-between;
gap: 14px; margin-bottom: 18px; flex-wrap: wrap;
}
.page-title { font-size: 1.45rem; font-weight: 800; letter-spacing: -0.02em; }
.page-date { color: var(--muted); font-size: 0.9rem; margin-top: 3px; }
.pill {
display: inline-flex; align-items: center; gap: 7px;
font-size: 0.78rem; font-weight: 600; padding: 6px 12px;
border-radius: 999px; background: var(--teal-50); color: var(--teal-d);
}
.pill--live .dot {
width: 8px; height: 8px; border-radius: 50%; background: var(--green);
box-shadow: 0 0 0 0 rgba(31, 157, 107, 0.5); animation: pulse 1.8s infinite;
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(31, 157, 107, 0.5); }
70% { box-shadow: 0 0 0 7px rgba(31, 157, 107, 0); }
100% { box-shadow: 0 0 0 0 rgba(31, 157, 107, 0); }
}
/* KPI cards */
.kpis {
display: grid; grid-template-columns: repeat(4, 1fr);
gap: 16px; margin-bottom: 18px;
}
.kpi {
background: var(--white); border: 1px solid var(--line);
border-radius: var(--radius); padding: 18px;
box-shadow: var(--shadow);
transition: transform .18s ease, box-shadow .18s ease;
}
.kpi:hover { transform: translateY(-3px); box-shadow: 0 18px 36px -20px rgba(15,127,115,.5); }
.kpi__top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.kpi__icon {
width: 40px; height: 40px; border-radius: 12px; font-size: 1.1rem;
display: grid; place-items: center;
}
.kpi__icon--teal { background: var(--teal-50); }
.kpi__icon--coral { background: #fff0e8; }
.delta {
font-size: 0.74rem; font-weight: 700; padding: 4px 9px; border-radius: 999px;
}
.delta--up { color: var(--green); background: #e7f6ee; }
.delta--down { color: var(--coral); background: #fff0e8; }
.kpi__value { font-size: 1.85rem; font-weight: 800; letter-spacing: -0.03em; line-height: 1.1; }
.kpi__label { color: var(--muted); font-size: 0.85rem; margin-top: 3px; }
/* Cards / grid */
.grid-2 {
display: grid; grid-template-columns: 1.25fr 1fr; gap: 16px; margin-bottom: 18px;
}
.grid-2--wide { grid-template-columns: 1fr 1.45fr; }
.card {
background: var(--white); border: 1px solid var(--line);
border-radius: var(--radius); padding: 18px; box-shadow: var(--shadow);
}
.card__head {
display: flex; align-items: flex-start; justify-content: space-between;
gap: 12px; margin-bottom: 16px;
}
.card__title { font-size: 1rem; font-weight: 700; }
.card__sub { color: var(--muted); font-size: 0.82rem; margin-top: 2px; }
/* Segmented control */
.seg {
display: inline-flex; background: var(--bg); border: 1px solid var(--line);
border-radius: 999px; padding: 3px;
}
.seg__btn {
border: 0; background: transparent; font: inherit; font-size: 0.8rem; font-weight: 600;
color: var(--muted); padding: 6px 14px; border-radius: 999px; cursor: pointer;
transition: background .18s, color .18s;
}
.seg__btn.is-active { background: var(--white); color: var(--teal-d); box-shadow: 0 2px 6px -2px rgba(0,0,0,.18); }
.seg__btn:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }
/* Bar chart */
.bars {
display: grid; grid-template-columns: repeat(7, 1fr); gap: 12px;
align-items: end; height: 200px; padding-top: 8px;
}
.bar { display: flex; flex-direction: column; align-items: center; gap: 8px; height: 100%; justify-content: flex-end; }
.bar__col {
width: 100%; max-width: 38px; border-radius: 10px 10px 4px 4px;
background: linear-gradient(180deg, var(--teal), var(--teal-d));
position: relative; height: 0; transition: height .6s cubic-bezier(.22,.9,.3,1);
}
.bar--today .bar__col { background: linear-gradient(180deg, var(--coral), #f56b3e); }
.bar__col::after {
content: attr(data-tip); position: absolute; top: -26px; left: 50%; transform: translateX(-50%);
background: var(--ink); color: #fff; font-size: 0.7rem; font-weight: 600;
padding: 3px 8px; border-radius: 7px; white-space: nowrap; opacity: 0; pointer-events: none;
transition: opacity .15s;
}
.bar:hover .bar__col::after { opacity: 1; }
.bar__day { font-size: 0.74rem; color: var(--muted); font-weight: 600; }
/* Breakdown list */
.breakdown { list-style: none; display: flex; flex-direction: column; gap: 14px; }
.breakdown__row { display: grid; grid-template-columns: 1fr auto; gap: 4px 10px; align-items: center; }
.breakdown__name { font-size: 0.86rem; font-weight: 600; display: flex; align-items: center; gap: 8px; }
.swatch { width: 10px; height: 10px; border-radius: 3px; flex: none; }
.breakdown__val { font-size: 0.82rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.breakdown__track {
grid-column: 1 / -1; height: 8px; border-radius: 999px; background: var(--bg); overflow: hidden;
}
.breakdown__fill { height: 100%; border-radius: 999px; width: 0; transition: width .7s cubic-bezier(.22,.9,.3,1); }
/* Utilization */
.util { list-style: none; display: flex; flex-direction: column; gap: 15px; }
.util__row { display: grid; gap: 6px; }
.util__top { display: flex; align-items: center; justify-content: space-between; }
.util__name { font-size: 0.85rem; font-weight: 600; }
.util__pct { font-size: 0.8rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.util__track { height: 9px; border-radius: 999px; background: var(--bg); overflow: hidden; }
.util__fill { height: 100%; border-radius: 999px; width: 0; transition: width .7s cubic-bezier(.22,.9,.3,1); }
.util__fill--ok { background: linear-gradient(90deg, var(--teal), var(--teal-d)); }
.util__fill--busy { background: linear-gradient(90deg, var(--amber), #d97f1f); }
.util__fill--full { background: linear-gradient(90deg, var(--coral), #f56b3e); }
/* Table */
.table-wrap { overflow-x: auto; }
.ptable { width: 100%; border-collapse: collapse; font-size: 0.86rem; }
.ptable thead th {
text-align: left; color: var(--muted); font-weight: 600; font-size: 0.74rem;
text-transform: uppercase; letter-spacing: .04em; padding: 0 12px 10px; white-space: nowrap;
}
.ptable tbody tr { border-top: 1px solid var(--line); transition: background .15s; }
.ptable tbody tr:hover { background: var(--teal-50); }
.ptable td { padding: 11px 12px; vertical-align: middle; }
.pet { display: flex; align-items: center; gap: 10px; }
.pet__ava {
width: 32px; height: 32px; border-radius: 10px; flex: none;
display: grid; place-items: center; font-size: 0.95rem; background: var(--teal-50);
}
.pet__name { font-weight: 700; }
.pet__species { color: var(--muted); font-size: 0.76rem; }
.status {
display: inline-flex; align-items: center; gap: 6px;
font-size: 0.74rem; font-weight: 700; padding: 4px 10px; border-radius: 999px; white-space: nowrap;
}
.status::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.status--checkedin { color: var(--blue); background: #e8f1fa; }
.status--exam { color: var(--amber); background: #fdf3e3; }
.status--ready { color: var(--green); background: #e7f6ee; }
.status--discharged { color: var(--muted); background: var(--bg); }
.empty { text-align: center; color: var(--muted); padding: 26px 0; font-size: 0.9rem; }
/* Responsive */
@media (max-width: 900px) {
.kpis { grid-template-columns: repeat(2, 1fr); }
.grid-2, .grid-2--wide { grid-template-columns: 1fr; }
}
@media (max-width: 520px) {
.app { padding: 16px 14px 36px; }
.search input { width: 130px; }
.kpis { grid-template-columns: 1fr 1fr; gap: 12px; }
.kpi { padding: 14px; }
.kpi__value { font-size: 1.5rem; }
.page-title { font-size: 1.25rem; }
.bars { height: 160px; gap: 7px; }
.bar__day { font-size: 0.66rem; }
.ptable thead { display: none; }
.ptable, .ptable tbody, .ptable tr, .ptable td { display: block; width: 100%; }
.ptable tbody tr { padding: 8px 0; }
.ptable td { padding: 4px 4px; display: flex; justify-content: space-between; gap: 12px; }
.ptable td::before {
content: attr(data-label); color: var(--muted); font-size: 0.72rem;
text-transform: uppercase; letter-spacing: .04em; font-weight: 600;
}
.pet { justify-content: flex-end; }
}(function () {
"use strict";
/* ---------- Data ---------- */
var chartData = {
visits: [
{ day: "Mon", value: 14, today: true },
{ day: "Tue", value: 22 },
{ day: "Wed", value: 19 },
{ day: "Thu", value: 27 },
{ day: "Fri", value: 31 },
{ day: "Sat", value: 38 },
{ day: "Sun", value: 9 }
],
revenue: [
{ day: "Mon", value: 2150, today: true },
{ day: "Tue", value: 3380 },
{ day: "Wed", value: 2910 },
{ day: "Thu", value: 4220 },
{ day: "Fri", value: 4980 },
{ day: "Sat", value: 6420 },
{ day: "Sun", value: 1480 }
]
};
var breakdown = [
{ name: "Wellness & vaccines", value: 64, color: "#179c8e" },
{ name: "Sick / urgent visit", value: 41, color: "#ff8a5c" },
{ name: "Dental", value: 28, color: "#3b82c4" },
{ name: "Surgery", value: 19, color: "#e0922f" },
{ name: "Grooming", value: 34, color: "#1f9d6b" }
];
var utilization = [
{ name: "Exam Room 1", pct: 92 },
{ name: "Exam Room 2", pct: 74 },
{ name: "Surgery Suite", pct: 48 },
{ name: "Dr. Mara Vance", pct: 88 },
{ name: "Dr. Theo Quill", pct: 66 }
];
var patients = [
{ emoji: "🐕", name: "Biscuit", species: "Labrador · 4y", owner: "Hannah Reyes", reason: "Annual wellness", vet: "Dr. Vance", status: "ready", statusLabel: "Ready" },
{ emoji: "🐈", name: "Mochi", species: "Tabby cat · 2y", owner: "Daniel Okafor", reason: "Vaccine booster", vet: "Dr. Quill", status: "discharged", statusLabel: "Discharged" },
{ emoji: "🐕", name: "Rufus", species: "Beagle · 7y", owner: "Priya Nair", reason: "Limping / X-ray", vet: "Dr. Vance", status: "exam", statusLabel: "In exam" },
{ emoji: "🐇", name: "Clover", species: "Lop rabbit · 1y", owner: "Marcus Webb", reason: "Dental check", vet: "Dr. Quill", status: "checkedin", statusLabel: "Checked in" },
{ emoji: "🐈", name: "Pixel", species: "Siamese · 5y", owner: "Sofia Bianchi", reason: "Skin irritation", vet: "Dr. Vance", status: "exam", statusLabel: "In exam" },
{ emoji: "🐕", name: "Tofu", species: "Corgi · 3y", owner: "Liam Carter", reason: "Spay surgery", vet: "Dr. Quill", status: "ready", statusLabel: "Ready" },
{ emoji: "🦜", name: "Kiwi", species: "Cockatiel · 6y", owner: "Elena Vargas", reason: "Wing trim", vet: "Dr. Vance", status: "discharged", statusLabel: "Discharged" },
{ emoji: "🐕", name: "Nova", species: "Husky · 2y", owner: "Jamal Pierce", reason: "Ear infection", vet: "Dr. Quill", status: "checkedin", statusLabel: "Checked in" }
];
var $ = function (s, c) { return (c || document).querySelector(s); };
/* ---------- Animated counters ---------- */
function animateCounters() {
var els = document.querySelectorAll(".kpi__value[data-count]");
els.forEach(function (el) {
var target = parseInt(el.getAttribute("data-count"), 10);
var prefix = el.getAttribute("data-prefix") || "";
var start = null;
var dur = 1100;
function step(ts) {
if (start === null) start = ts;
var p = Math.min((ts - start) / dur, 1);
var eased = 1 - Math.pow(1 - p, 3);
var val = Math.round(target * eased);
el.textContent = prefix + val.toLocaleString("en-US");
if (p < 1) requestAnimationFrame(step);
}
requestAnimationFrame(step);
});
}
/* ---------- Bar chart ---------- */
var currentMetric = "visits";
function renderBars(metric) {
var rows = chartData[metric];
var max = Math.max.apply(null, rows.map(function (r) { return r.value; }));
var wrap = $("#bars");
wrap.innerHTML = "";
rows.forEach(function (r, i) {
var bar = document.createElement("div");
bar.className = "bar" + (r.today ? " bar--today" : "");
var col = document.createElement("div");
col.className = "bar__col";
var tip = metric === "revenue" ? "$" + r.value.toLocaleString("en-US") : r.value + " visits";
col.setAttribute("data-tip", tip);
var day = document.createElement("span");
day.className = "bar__day";
day.textContent = r.day;
bar.appendChild(col);
bar.appendChild(day);
wrap.appendChild(bar);
var h = Math.round((r.value / max) * 100);
(function (c, height, delay) {
setTimeout(function () { c.style.height = height + "%"; }, delay);
})(col, h, 60 + i * 55);
});
$("#chart-sub").textContent = metric === "revenue" ? "Revenue by day" : "Appointments by day";
}
/* ---------- Breakdown ---------- */
function renderBreakdown() {
var total = breakdown.reduce(function (a, b) { return a + b.value; }, 0);
var ul = $("#breakdown");
ul.innerHTML = "";
breakdown.forEach(function (b, i) {
var li = document.createElement("li");
li.className = "breakdown__row";
var pct = Math.round((b.value / total) * 100);
li.innerHTML =
'<span class="breakdown__name"><span class="swatch" style="background:' + b.color + '"></span>' + b.name + "</span>" +
'<span class="breakdown__val">' + b.value + " · " + pct + "%</span>" +
'<span class="breakdown__track"><span class="breakdown__fill" style="background:' + b.color + '"></span></span>';
ul.appendChild(li);
(function (fill, p, delay) {
setTimeout(function () { fill.style.width = p + "%"; }, delay);
})(li.querySelector(".breakdown__fill"), pct, 120 + i * 70);
});
}
/* ---------- Utilization ---------- */
function renderUtil() {
var ul = $("#util");
ul.innerHTML = "";
utilization.forEach(function (u, i) {
var cls = u.pct >= 85 ? "util__fill--full" : u.pct >= 65 ? "util__fill--busy" : "util__fill--ok";
var li = document.createElement("li");
li.className = "util__row";
li.innerHTML =
'<div class="util__top"><span class="util__name">' + u.name + "</span>" +
'<span class="util__pct">' + u.pct + "%</span></div>" +
'<div class="util__track"><span class="util__fill ' + cls + '"></span></div>';
ul.appendChild(li);
(function (fill, p, delay) {
setTimeout(function () { fill.style.width = p + "%"; }, delay);
})(li.querySelector(".util__fill"), u.pct, 150 + i * 70);
});
}
/* ---------- Patients table ---------- */
function renderTable(filter) {
var q = (filter || "").trim().toLowerCase();
var body = $("#ptable-body");
body.innerHTML = "";
var shown = 0;
patients.forEach(function (p) {
if (q && (p.name + " " + p.owner + " " + p.species + " " + p.reason).toLowerCase().indexOf(q) === -1) return;
shown++;
var tr = document.createElement("tr");
tr.innerHTML =
'<td data-label="Patient"><span class="pet"><span class="pet__ava" aria-hidden="true">' + p.emoji + "</span>" +
'<span><span class="pet__name">' + p.name + '</span><br><span class="pet__species">' + p.species + "</span></span></span></td>" +
'<td data-label="Owner">' + p.owner + "</td>" +
'<td data-label="Reason">' + p.reason + "</td>" +
'<td data-label="Vet">' + p.vet + "</td>" +
'<td data-label="Status"><span class="status status--' + p.status + '">' + p.statusLabel + "</span></td>";
body.appendChild(tr);
});
$("#row-count").textContent = shown;
$("#empty").hidden = shown !== 0;
}
/* ---------- Wire up ---------- */
document.querySelectorAll(".seg__btn").forEach(function (btn) {
btn.addEventListener("click", function () {
if (btn.classList.contains("is-active")) return;
document.querySelectorAll(".seg__btn").forEach(function (b) {
b.classList.remove("is-active");
b.setAttribute("aria-selected", "false");
});
btn.classList.add("is-active");
btn.setAttribute("aria-selected", "true");
currentMetric = btn.getAttribute("data-metric");
renderBars(currentMetric);
});
});
var search = $("#patient-search");
if (search) search.addEventListener("input", function () { renderTable(search.value); });
/* ---------- Init ---------- */
animateCounters();
renderBars(currentMetric);
renderBreakdown();
renderUtil();
renderTable("");
})();<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Pawsridge Veterinary — Clinic Dashboard</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>
<div class="app">
<header class="topbar">
<div class="brand">
<span class="brand__logo" aria-hidden="true">
<svg viewBox="0 0 24 24" width="22" height="22" fill="currentColor">
<circle cx="6" cy="10" r="2"/><circle cx="10.5" cy="6.5" r="2"/>
<circle cx="15.5" cy="6.5" r="2"/><circle cx="19" cy="10.5" r="2"/>
<path d="M12 12c-3 0-5.5 2.2-5.5 4.6 0 1.7 1.4 2.9 3.2 2.9.9 0 1.6-.4 2.3-.4s1.4.4 2.3.4c1.8 0 3.2-1.2 3.2-2.9C17.5 14.2 15 12 12 12z"/>
</svg>
</span>
<div>
<h1 class="brand__name">Pawsridge Veterinary</h1>
<p class="brand__sub">Clinic dashboard</p>
</div>
</div>
<div class="topbar__right">
<label class="search" for="patient-search">
<svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true"><path fill="currentColor" d="M21 20l-5.6-5.6a7 7 0 10-1.4 1.4L20 21zM5 10a5 5 0 1110 0 5 5 0 01-10 0z"/></svg>
<input id="patient-search" type="search" placeholder="Search patients or owners…" autocomplete="off" />
</label>
<div class="avatar" title="Dr. Mara Vance">MV</div>
</div>
</header>
<main class="content">
<div class="page-head">
<div>
<h2 class="page-title">Good morning, Dr. Vance</h2>
<p class="page-date" id="today-label">Monday, June 22 · 14 appointments scheduled</p>
</div>
<span class="pill pill--live"><span class="dot"></span> Live</span>
</div>
<!-- KPI cards -->
<section class="kpis" aria-label="Key metrics">
<article class="kpi">
<div class="kpi__top">
<span class="kpi__icon kpi__icon--teal" aria-hidden="true">📅</span>
<span class="delta delta--up">▲ 12%</span>
</div>
<p class="kpi__value" data-count="14">0</p>
<p class="kpi__label">Appointments today</p>
</article>
<article class="kpi">
<div class="kpi__top">
<span class="kpi__icon kpi__icon--coral" aria-hidden="true">🐾</span>
<span class="delta delta--up">▲ 8%</span>
</div>
<p class="kpi__value" data-count="186">0</p>
<p class="kpi__label">Patients seen this week</p>
</article>
<article class="kpi">
<div class="kpi__top">
<span class="kpi__icon kpi__icon--teal" aria-hidden="true">💳</span>
<span class="delta delta--up">▲ 5%</span>
</div>
<p class="kpi__value" data-count="28640" data-prefix="$">0</p>
<p class="kpi__label">Revenue this week</p>
</article>
<article class="kpi">
<div class="kpi__top">
<span class="kpi__icon kpi__icon--coral" aria-hidden="true">❤️</span>
<span class="delta delta--down">▼ 3%</span>
</div>
<p class="kpi__value" data-count="17">0</p>
<p class="kpi__label">New patients</p>
</article>
</section>
<!-- Charts row -->
<section class="grid-2">
<article class="card chart-card">
<header class="card__head">
<div>
<h3 class="card__title">This week</h3>
<p class="card__sub" id="chart-sub">Appointments by day</p>
</div>
<div class="seg" role="tablist" aria-label="Chart metric">
<button class="seg__btn is-active" role="tab" aria-selected="true" data-metric="visits">Visits</button>
<button class="seg__btn" role="tab" aria-selected="false" data-metric="revenue">Revenue</button>
</div>
</header>
<div class="bars" id="bars" aria-hidden="true"></div>
</article>
<article class="card">
<header class="card__head">
<div>
<h3 class="card__title">By appointment type</h3>
<p class="card__sub">This week · 186 visits</p>
</div>
</header>
<ul class="breakdown" id="breakdown"></ul>
</article>
</section>
<!-- Utilization + table -->
<section class="grid-2 grid-2--wide">
<article class="card">
<header class="card__head">
<div>
<h3 class="card__title">Room & vet utilization</h3>
<p class="card__sub">Today, so far</p>
</div>
</header>
<ul class="util" id="util"></ul>
</article>
<article class="card">
<header class="card__head">
<div>
<h3 class="card__title">Recent patients</h3>
<p class="card__sub"><span id="row-count">8</span> showing</p>
</div>
</header>
<div class="table-wrap">
<table class="ptable">
<caption class="sr-only">Recent patients seen today</caption>
<thead>
<tr>
<th scope="col">Patient</th>
<th scope="col">Owner</th>
<th scope="col">Reason</th>
<th scope="col">Vet</th>
<th scope="col">Status</th>
</tr>
</thead>
<tbody id="ptable-body"></tbody>
</table>
<p class="empty" id="empty" hidden>No patients match your search.</p>
</div>
</article>
</section>
</main>
</div>
<script src="script.js"></script>
</body>
</html>Clinic Dashboard
A warm, at-a-glance admin view for a veterinary practice. Four KPI cards lead with animated counters for appointments today, patients seen this week, revenue and new patients, each with a delta badge versus last week. Below, a pure-CSS bar chart plots appointments across the week, an appointments-by-type breakdown shows the case mix, and utilization meters track how busy each exam room and vet is.
The recent-patients table lists real-sounding pets, owners, species, visit reason and a status badge (Checked in, In exam, Ready, Discharged). A search box filters rows live and a segmented control switches the bar chart between visits and revenue, all in vanilla JS with animated transitions.
The teal-and-coral palette, soft rounded cards and paw iconography keep it approachable while staying dense enough for a real ops screen.
Illustrative UI only — not for real veterinary use.