Dental — Dentist Profiles
A reassuring dentist-team directory built from clean clinical-light profile cards. Each dentist shows a portrait, specialty tag, star rating, years in practice and language chips, with credential pills for degrees and memberships. An inline bio expands smoothly on demand, a specialty filter and search narrow the roster live, and every card carries a book-with-them button that fires a friendly confirmation toast. Fully responsive, keyboard-navigable and accessible down to narrow phones.
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;
--shadow-sm: 0 1px 2px rgba(15, 39, 64, 0.06), 0 2px 8px rgba(15, 39, 64, 0.05);
--shadow-md: 0 6px 22px rgba(15, 39, 64, 0.08), 0 2px 6px rgba(15, 39, 64, 0.05);
--shadow-lg: 0 18px 48px rgba(15, 39, 64, 0.14);
}
* { 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(1100px 480px at 12% -8%, var(--blue-50), transparent 60%),
radial-gradient(900px 420px at 110% 4%, var(--mint-50), transparent 55%),
var(--bg);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
min-height: 100vh;
}
.page {
max-width: 1120px;
margin: 0 auto;
padding: 22px 22px 64px;
}
/* Masthead */
.masthead {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 12px 16px;
background: rgba(255, 255, 255, 0.72);
backdrop-filter: blur(10px);
border: 1px solid var(--line);
border-radius: var(--r-lg);
box-shadow: var(--shadow-sm);
}
.brand { display: flex; align-items: center; gap: 12px; }
.brand-mark {
display: grid;
place-items: center;
width: 40px;
height: 40px;
border-radius: 12px;
color: var(--white);
background: linear-gradient(150deg, var(--blue), var(--blue-d));
box-shadow: 0 6px 14px rgba(37, 99, 235, 0.32);
}
.brand-name { margin: 0; font-weight: 800; letter-spacing: -0.01em; }
.brand-sub { margin: 0; font-size: 12.5px; color: var(--muted); }
.phone-cta {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 16px;
border-radius: 999px;
font-weight: 600;
font-size: 14px;
color: var(--blue-d);
text-decoration: none;
background: var(--blue-50);
border: 1px solid rgba(37, 99, 235, 0.22);
transition: transform .15s ease, background .15s ease, box-shadow .15s ease;
}
.phone-cta:hover { background: #e2edff; transform: translateY(-1px); box-shadow: var(--shadow-sm); }
.phone-cta:active { transform: translateY(0); }
/* Intro */
.intro { padding: 40px 6px 26px; max-width: 680px; }
.eyebrow {
margin: 0 0 10px;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--blue);
}
.intro h1 {
margin: 0 0 12px;
font-family: "Fraunces", Georgia, serif;
font-weight: 600;
font-size: clamp(28px, 5vw, 44px);
line-height: 1.08;
letter-spacing: -0.015em;
color: var(--ink);
}
.lede { margin: 0 0 22px; font-size: 16.5px; color: var(--ink-2); }
.trust-row {
display: flex;
flex-wrap: wrap;
gap: 10px 26px;
margin: 0;
padding: 0;
list-style: none;
}
.trust-row li { font-size: 13.5px; color: var(--muted); }
.trust-row strong { display: block; font-size: 20px; font-weight: 800; color: var(--ink); font-variant-numeric: tabular-nums; }
/* Controls */
.controls {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 12px;
margin: 10px 0 8px;
}
.search {
position: relative;
display: flex;
align-items: center;
flex: 1 1 260px;
min-width: 220px;
}
.search svg { position: absolute; left: 14px; color: var(--muted); pointer-events: none; }
.search input {
width: 100%;
padding: 12px 14px 12px 42px;
border-radius: 999px;
border: 1px solid var(--line-2);
background: var(--white);
font: inherit;
font-size: 15px;
color: var(--ink);
box-shadow: var(--shadow-sm);
transition: border-color .15s ease, box-shadow .15s ease;
}
.search input::placeholder { color: var(--muted); }
.search input:focus-visible {
outline: none;
border-color: var(--blue);
box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.16);
}
.filters { display: flex; flex-wrap: wrap; gap: 8px; }
.chip {
padding: 9px 15px;
border-radius: 999px;
border: 1px solid var(--line-2);
background: var(--white);
color: var(--ink-2);
font: inherit;
font-size: 13.5px;
font-weight: 600;
cursor: pointer;
transition: transform .12s ease, background .15s ease, color .15s ease, border-color .15s ease, box-shadow .15s ease;
}
.chip:hover { border-color: var(--blue); color: var(--blue-d); transform: translateY(-1px); }
.chip:focus-visible { outline: none; box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.16); }
.chip.is-active {
background: linear-gradient(150deg, var(--blue), var(--blue-d));
color: var(--white);
border-color: transparent;
box-shadow: 0 6px 14px rgba(37, 99, 235, 0.28);
}
.result-line { margin: 16px 6px 18px; font-size: 13.5px; color: var(--muted); }
.result-line span { font-weight: 700; color: var(--ink); }
/* Grid */
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 20px;
}
.card {
display: flex;
flex-direction: column;
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-lg);
box-shadow: var(--shadow-sm);
overflow: hidden;
transition: transform .2s ease, box-shadow .2s ease, border-color .2s ease;
animation: rise .4s ease both;
}
.card:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); border-color: rgba(37, 99, 235, 0.24); }
.card:focus-visible { outline: none; box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.18), var(--shadow-md); }
.card.is-hidden { display: none; }
@keyframes rise {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.card-top {
position: relative;
height: 132px;
display: flex;
align-items: flex-end;
padding: 16px;
background:
radial-gradient(140px 90px at 82% 10%, rgba(52, 211, 153, 0.28), transparent 70%),
linear-gradient(150deg, #dbe8ff, #eef5ff);
}
.avatar {
width: 74px;
height: 74px;
border-radius: 50%;
background-size: cover;
background-position: center;
border: 3px solid var(--white);
box-shadow: 0 6px 16px rgba(15, 39, 64, 0.18);
}
.status {
position: absolute;
top: 14px;
left: 16px;
display: inline-flex;
align-items: center;
gap: 6px;
padding: 5px 11px;
border-radius: 999px;
font-size: 11.5px;
font-weight: 700;
background: rgba(255, 255, 255, 0.86);
color: var(--ink-2);
box-shadow: var(--shadow-sm);
}
.status::before {
content: "";
width: 7px; height: 7px;
border-radius: 50%;
background: var(--ok);
}
.status.soon::before { background: var(--warn); }
.status.soon { color: var(--warn); }
.card-body { display: flex; flex-direction: column; gap: 10px; padding: 16px 18px 18px; }
.name-row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.name { margin: 0; font-size: 18px; font-weight: 700; letter-spacing: -0.01em; color: var(--ink); }
.rating {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 13px;
font-weight: 700;
color: var(--warn);
}
.rating .rating-val { color: var(--ink); }
.role { margin: 0; font-size: 13.5px; font-weight: 600; color: var(--blue-d); }
.meta { display: flex; flex-wrap: wrap; gap: 6px 14px; margin: 2px 0 2px; padding: 0; list-style: none; }
.meta li { font-size: 12.5px; color: var(--muted); display: inline-flex; align-items: center; gap: 5px; }
.meta li strong { color: var(--ink-2); font-weight: 600; }
.creds { display: flex; flex-wrap: wrap; gap: 6px; margin: 4px 0 2px; padding: 0; list-style: none; }
.creds li {
font-size: 11.5px;
font-weight: 600;
color: var(--ink-2);
background: var(--blue-50);
border: 1px solid rgba(37, 99, 235, 0.16);
padding: 4px 9px;
border-radius: 999px;
}
.bio {
border-top: 1px dashed var(--line-2);
padding-top: 12px;
margin-top: 2px;
animation: fade .25s ease both;
}
@keyframes fade { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } }
.bio-text { margin: 0 0 10px; font-size: 13.5px; color: var(--ink-2); }
.langs { display: flex; flex-wrap: wrap; gap: 6px; margin: 0; padding: 0; list-style: none; }
.langs li {
font-size: 11px;
font-weight: 600;
color: #128a63;
background: var(--mint-50);
border: 1px solid rgba(52, 211, 153, 0.32);
padding: 3px 9px;
border-radius: 999px;
}
.actions { display: flex; gap: 10px; margin-top: 12px; }
.btn {
flex: 1;
padding: 11px 12px;
border-radius: var(--r-md);
font: inherit;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: transform .14s ease, background .15s ease, box-shadow .15s ease, color .15s ease, border-color .15s ease;
}
.btn:focus-visible { outline: none; box-shadow: 0 0 0 4px rgba(37, 99, 235, 0.2); }
.btn-book {
color: var(--white);
border: 1px solid transparent;
background: linear-gradient(150deg, var(--blue), var(--blue-d));
box-shadow: 0 6px 14px rgba(37, 99, 235, 0.26);
}
.btn-book:hover { transform: translateY(-1px); box-shadow: 0 10px 20px rgba(37, 99, 235, 0.32); }
.btn-book:active { transform: translateY(0); }
.btn-bio {
flex: 0 0 auto;
color: var(--ink-2);
background: var(--white);
border: 1px solid var(--line-2);
}
.btn-bio:hover { border-color: var(--blue); color: var(--blue-d); }
.btn-bio[aria-expanded="true"] { background: var(--blue-50); color: var(--blue-d); border-color: rgba(37, 99, 235, 0.28); }
/* Empty */
.empty {
margin: 30px 6px;
padding: 30px;
text-align: center;
color: var(--muted);
background: var(--white);
border: 1px dashed var(--line-2);
border-radius: var(--r-lg);
}
.empty button {
border: none;
background: none;
color: var(--blue-d);
font: inherit;
font-weight: 700;
cursor: pointer;
text-decoration: underline;
}
.foot { margin-top: 36px; text-align: center; font-size: 12.5px; color: var(--muted); }
/* Toast */
.toast {
position: fixed;
left: 50%;
bottom: 26px;
transform: translateX(-50%) translateY(16px);
background: var(--ink);
color: var(--white);
padding: 13px 20px;
border-radius: var(--r-md);
font-size: 14px;
font-weight: 500;
box-shadow: var(--shadow-lg);
opacity: 0;
pointer-events: none;
transition: opacity .25s ease, transform .25s ease;
z-index: 60;
max-width: min(92vw, 420px);
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
.toast::before {
content: "✓";
display: inline-block;
margin-right: 9px;
color: var(--mint);
font-weight: 800;
}
/* Responsive */
@media (max-width: 520px) {
.page { padding: 14px 14px 48px; }
.masthead { border-radius: var(--r-md); padding: 10px 12px; }
.brand-sub { display: none; }
.intro { padding: 26px 2px 18px; }
.grid { grid-template-columns: 1fr; gap: 16px; }
.filters { width: 100%; overflow-x: auto; flex-wrap: nowrap; padding-bottom: 4px; -webkit-overflow-scrolling: touch; }
.chip { flex: 0 0 auto; }
.trust-row { gap: 10px 18px; }
.actions { flex-direction: column; }
.btn-bio { flex: 1; }
.toast { bottom: 16px; }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}(function () {
"use strict";
var U = "https://images.unsplash.com/";
var Q = "?auto=format&fit=crop&w=200&q=70";
var DENTISTS = [
{
name: "Dr. Elena Vargas",
role: "General & Preventive Dentistry",
filter: "general",
rating: "4.9",
years: 16,
next: "Tomorrow",
soon: false,
photo: U + "photo-1559839734-2b71ea197ec2" + Q,
creds: ["DDS, State University", "AGD Fellow", "Invisalign Certified"],
langs: ["English", "Spanish"],
bio: "Elena leads our preventive care team and believes the calmest visits come from clear explanations. She focuses on gentle cleanings, early cavity detection, and building lifelong habits with nervous first-timers."
},
{
name: "Dr. Marcus Bell",
role: "Orthodontics",
filter: "ortho",
rating: "4.8",
years: 12,
next: "This week",
soon: false,
photo: U + "photo-1612349317150-e413f6a5b16d" + Q,
creds: ["DMD", "MS Orthodontics", "Clear-Aligner Provider"],
langs: ["English", "French"],
bio: "Marcus straightens smiles with modern aligners and low-profile braces. He designs treatment plans you can preview before you commit, and he is known for patient-friendly progress check-ins."
},
{
name: "Dr. Priya Anand",
role: "Pediatric Dentistry",
filter: "pediatric",
rating: "5.0",
years: 9,
next: "Tomorrow",
soon: false,
photo: U + "photo-1594824476967-48c8b964273f" + Q,
creds: ["DDS", "Pediatric Residency", "Board Certified"],
langs: ["English", "Hindi", "Gujarati"],
bio: "Priya makes the chair feel like an adventure. She specialises in first visits, sealants, and helping anxious kids and parents leave with a smile and a sticker."
},
{
name: "Dr. James Okafor",
role: "Oral & Maxillofacial Surgery",
filter: "surgery",
rating: "4.9",
years: 18,
next: "Next week",
soon: true,
photo: U + "photo-1622253692010-333f2da6031d" + Q,
creds: ["DDS, MD", "OMFS Residency", "IV Sedation Licensed"],
langs: ["English"],
bio: "James handles extractions, implants, and complex cases with a steady, reassuring manner. He walks every patient through sedation options so surgery day feels predictable and safe."
},
{
name: "Dr. Sofia Larsen",
role: "Cosmetic & Restorative",
filter: "cosmetic",
rating: "4.9",
years: 11,
next: "This week",
soon: false,
photo: U + "photo-1573496359142-b8d87734a5a2" + Q,
creds: ["DMD", "Aesthetic Dentistry Cert.", "Veneer Specialist"],
langs: ["English", "Swedish"],
bio: "Sofia designs natural-looking veneers, whitening, and bonding. She uses digital previews so you can see your new smile before a single tooth is touched."
},
{
name: "Dr. Amir Hassan",
role: "General & Endodontics",
filter: "general",
rating: "4.7",
years: 14,
next: "Today",
soon: false,
photo: U + "photo-1537368910025-700350fe46c7" + Q,
creds: ["DDS", "Endodontic Training", "Same-Day Crowns"],
langs: ["English", "Arabic"],
bio: "Amir keeps painful problems from escalating. He covers root canals, urgent toothaches, and same-day crowns, and he saves same-week slots for emergencies."
},
{
name: "Dr. Nina Petrova",
role: "Orthodontics",
filter: "ortho",
rating: "4.8",
years: 8,
next: "This week",
soon: false,
photo: U + "photo-1580281658626-ee379f3cce93" + Q,
creds: ["DMD", "Orthodontic Residency", "Lingual Braces"],
langs: ["English", "Russian"],
bio: "Nina works with teens and adults who want discreet options. She is meticulous about the finishing details that make a bite feel right, not just look straight."
},
{
name: "Dr. Theo Nakamura",
role: "Cosmetic & Whitening",
filter: "cosmetic",
rating: "4.9",
years: 10,
next: "Tomorrow",
soon: false,
photo: U + "photo-1566492031773-4f4e44671857" + Q,
creds: ["DDS", "Smile Design Cert.", "In-Office Whitening"],
langs: ["English", "Japanese"],
bio: "Theo blends art and precision for brighter, balanced smiles. He is a favourite for wedding-ready whitening and subtle bonding that nobody can spot."
}
];
var grid = document.getElementById("grid");
var tpl = document.getElementById("card-tpl");
var searchInput = document.getElementById("search");
var chips = Array.prototype.slice.call(document.querySelectorAll(".chip"));
var resultCount = document.getElementById("result-count");
var emptyEl = document.getElementById("empty");
var resetBtn = document.getElementById("reset");
var state = { filter: "all", query: "" };
/* Toast helper */
var toastEl = document.getElementById("toast");
var toastTimer;
function toast(msg) {
if (!toastEl) return;
toastEl.hidden = false;
toastEl.textContent = msg;
// reflow to restart transition
void toastEl.offsetWidth;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("show");
}, 2600);
}
function buildCard(d, i) {
var node = tpl.content.firstElementChild.cloneNode(true);
node.dataset.filter = d.filter;
node.dataset.name = d.name.toLowerCase();
node.dataset.role = d.role.toLowerCase();
node.style.animationDelay = Math.min(i * 40, 280) + "ms";
node.querySelector(".avatar").style.backgroundImage = "url('" + d.photo + "')";
var status = node.querySelector(".status");
status.textContent = "Next: " + d.next;
if (d.soon) status.classList.add("soon");
node.querySelector(".name").textContent = d.name;
node.querySelector(".rating-val").textContent = d.rating;
node.querySelector(".role").textContent = d.role;
var meta = node.querySelector(".meta");
meta.innerHTML =
"<li><strong>" + d.years + "</strong> yrs experience</li>" +
"<li>Accepting new patients</li>";
var creds = node.querySelector(".creds");
d.creds.forEach(function (c) {
var li = document.createElement("li");
li.textContent = c;
creds.appendChild(li);
});
node.querySelector(".bio-text").textContent = d.bio;
var langs = node.querySelector(".langs");
d.langs.forEach(function (l) {
var li = document.createElement("li");
li.textContent = l;
langs.appendChild(li);
});
var bio = node.querySelector(".bio");
var bioBtn = node.querySelector(".btn-bio");
bioBtn.addEventListener("click", function () {
var open = bioBtn.getAttribute("aria-expanded") === "true";
bioBtn.setAttribute("aria-expanded", String(!open));
bio.hidden = open;
bioBtn.textContent = open ? "Read bio" : "Hide bio";
});
node.querySelector(".btn-book").addEventListener("click", function () {
toast("Request sent — " + d.name + " will confirm your appointment shortly.");
});
return node;
}
function render() {
var frag = document.createDocumentFragment();
DENTISTS.forEach(function (d, i) {
frag.appendChild(buildCard(d, i));
});
grid.appendChild(frag);
}
function applyFilters() {
var cards = grid.querySelectorAll(".card");
var shown = 0;
cards.forEach(function (card) {
var matchFilter = state.filter === "all" || card.dataset.filter === state.filter;
var q = state.query;
var matchQuery = !q || card.dataset.name.indexOf(q) > -1 || card.dataset.role.indexOf(q) > -1;
var visible = matchFilter && matchQuery;
card.classList.toggle("is-hidden", !visible);
if (visible) shown++;
});
resultCount.textContent = shown;
emptyEl.hidden = shown !== 0;
}
chips.forEach(function (chip) {
chip.addEventListener("click", function () {
chips.forEach(function (c) {
c.classList.remove("is-active");
c.setAttribute("aria-selected", "false");
});
chip.classList.add("is-active");
chip.setAttribute("aria-selected", "true");
state.filter = chip.dataset.filter;
applyFilters();
});
});
searchInput.addEventListener("input", function () {
state.query = searchInput.value.trim().toLowerCase();
applyFilters();
});
resetBtn.addEventListener("click", function () {
state.filter = "all";
state.query = "";
searchInput.value = "";
chips.forEach(function (c) {
var isAll = c.dataset.filter === "all";
c.classList.toggle("is-active", isAll);
c.setAttribute("aria-selected", String(isAll));
});
applyFilters();
});
document.querySelectorAll("[data-toast]").forEach(function (el) {
el.addEventListener("click", function (e) {
e.preventDefault();
toast(el.getAttribute("data-toast"));
});
});
render();
applyFilters();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Brightsmile Dental — Meet the Team</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&family=Fraunces:opsz,[email protected],500;9..144,600&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="page">
<header class="masthead">
<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 3c-2 0-3 1-5 1S4 3 3 4c-1 2 0 6 1 9 .8 2.4 1.4 5 3 5 1.2 0 1.3-2 2-4h2c.7 2 .8 4 2 4 1.6 0 2.2-2.6 3-5 1-3 2-7 1-9-1-1-2-1-4-0s-3-1-5-1Z"/></svg>
</span>
<div>
<p class="brand-name">Brightsmile Dental</p>
<p class="brand-sub">Riverside Family Practice</p>
</div>
</div>
<a class="phone-cta" href="#" data-toast="Reception: (555) 018-2240 — open 8am–6pm">
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 16.9v3a2 2 0 0 1-2.2 2 19.8 19.8 0 0 1-8.6-3 19.5 19.5 0 0 1-6-6 19.8 19.8 0 0 1-3-8.6A2 2 0 0 1 4.1 2h3a2 2 0 0 1 2 1.7c.1.9.4 1.8.7 2.7a2 2 0 0 1-.5 2.1L8.1 9.9a16 16 0 0 0 6 6l1.4-1.2a2 2 0 0 1 2.1-.5c.9.3 1.8.6 2.7.7a2 2 0 0 1 1.7 2Z"/></svg>
Call us
</a>
</header>
<section class="intro">
<p class="eyebrow">Our clinicians</p>
<h1>Meet the people behind your smile</h1>
<p class="lede">A gentle, experienced team of dentists and specialists. Filter by focus, read a full bio, and book directly with the clinician who fits your care.</p>
<ul class="trust-row" aria-label="Practice highlights">
<li><strong>12</strong> clinicians</li>
<li><strong>4.9</strong> avg rating</li>
<li><strong>26k+</strong> patients cared for</li>
<li><strong>9</strong> languages spoken</li>
</ul>
</section>
<div class="controls">
<div class="search">
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg>
<input id="search" type="search" placeholder="Search by name or focus…" aria-label="Search dentists" autocomplete="off" />
</div>
<div class="filters" role="tablist" aria-label="Filter by specialty">
<button class="chip is-active" role="tab" aria-selected="true" data-filter="all">All</button>
<button class="chip" role="tab" aria-selected="false" data-filter="general">General</button>
<button class="chip" role="tab" aria-selected="false" data-filter="ortho">Orthodontics</button>
<button class="chip" role="tab" aria-selected="false" data-filter="pediatric">Pediatric</button>
<button class="chip" role="tab" aria-selected="false" data-filter="surgery">Oral surgery</button>
<button class="chip" role="tab" aria-selected="false" data-filter="cosmetic">Cosmetic</button>
</div>
</div>
<p class="result-line" aria-live="polite"><span id="result-count">6</span> clinicians available</p>
<main id="grid" class="grid"></main>
<p class="empty" id="empty" hidden>No clinicians match that search. <button type="button" id="reset">Clear filters</button></p>
<footer class="foot">
<p>Brightsmile Dental · 480 Riverside Ave · Same-week appointments available</p>
</footer>
</div>
<div class="toast" id="toast" role="status" aria-live="polite" hidden></div>
<template id="card-tpl">
<article class="card" tabindex="-1">
<div class="card-top">
<span class="avatar" aria-hidden="true"></span>
<span class="status"></span>
</div>
<div class="card-body">
<div class="name-row">
<h2 class="name"></h2>
<span class="rating"><svg viewBox="0 0 24 24" width="14" height="14" fill="currentColor" aria-hidden="true"><path d="M12 2l2.9 6 6.6.9-4.8 4.6 1.2 6.5L12 17.8 6.1 20l1.2-6.5L2.5 8.9 9.1 8 12 2z"/></svg><span class="rating-val"></span></span>
</div>
<p class="role"></p>
<ul class="meta"></ul>
<ul class="creds"></ul>
<div class="bio" hidden>
<p class="bio-text"></p>
<ul class="langs" aria-label="Languages"></ul>
</div>
<div class="actions">
<button class="btn btn-book" type="button">Book with them</button>
<button class="btn btn-bio" type="button" aria-expanded="false">Read bio</button>
</div>
</div>
</article>
</template>
<script src="script.js"></script>
</body>
</html>A calm, trust-first team page for a fictional dental practice. Each clinician is presented on a rounded profile card with a soft portrait, a specialty tag, a star rating, and quick facts like years in practice and next availability. Credential pills summarise degrees and professional memberships at a glance, while language chips reassure patients they can be seen in their own tongue.
Interaction stays lightweight and friendly. A specialty filter bar plus a live search box narrow the roster instantly, updating a running result count. Every card has a Read bio toggle that expands an accessible, animated panel with a longer introduction, and a primary Book with them button that raises a small confirmation toast. Hover, active, and focus states are all handled, so the page reads well with a mouse or a keyboard.
The whole thing is vanilla HTML, CSS, and JS — no frameworks, no build step. It collapses gracefully to a single column on small screens, respects reduced-motion preferences, and keeps WCAG-friendly contrast throughout the clinical-light palette.
Illustrative UI only — not intended for real medical use.