Dental — New Patient Intake
A sectioned new-patient intake form for a dental practice covering personal details, medical and dental history checkboxes, insurance, and consent — with inline validation, a live progress bar, section navigation, and a friendly review summary before submit.
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,.06);
--sh-md:0 10px 30px rgba(15,39,64,.08);
--sh-lg:0 24px 60px rgba(15,39,64,.12);
}
*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
body{
margin:0; background:
radial-gradient(1100px 520px at 12% -8%, var(--blue-50), transparent 60%),
radial-gradient(900px 480px at 100% 0%, var(--mint-50), transparent 55%),
var(--bg);
color:var(--ink); line-height:1.5;
font-family:"Inter",system-ui,-apple-system,sans-serif;
-webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility;
min-height:100vh;
}
.shell{max-width:1040px; margin:0 auto; padding:28px 20px 64px}
/* Topbar */
.topbar{display:flex; align-items:center; justify-content:space-between; gap:16px; flex-wrap:wrap; margin-bottom:16px}
.brand{display:flex; align-items:center; gap:12px}
.brand__mark{
width:44px; height:44px; border-radius:13px; display:grid; place-items:center;
color:#fff; background:linear-gradient(150deg,var(--blue),var(--blue-d));
box-shadow:0 8px 20px rgba(37,99,235,.32);
}
.brand__txt{display:flex; flex-direction:column; line-height:1.2}
.brand__txt strong{font-size:1.05rem; font-weight:800; letter-spacing:-.01em}
.brand__txt span{font-size:.82rem; color:var(--muted); font-weight:500}
.topbar__meta{display:flex; align-items:center; gap:14px}
.pill{display:inline-flex; align-items:center; gap:7px; font-size:.78rem; font-weight:600; padding:6px 12px; border-radius:999px}
.pill--ok{background:var(--mint-50); color:var(--ok)}
.dot{width:7px; height:7px; border-radius:50%; background:var(--ok); box-shadow:0 0 0 3px rgba(34,176,125,.18)}
.save{font-size:.78rem; color:var(--muted); font-weight:500}
/* Progress */
.progress{height:8px; border-radius:999px; background:rgba(15,39,64,.08); overflow:hidden; margin-bottom:24px}
.progress__fill{height:100%; width:0; border-radius:999px; background:linear-gradient(90deg,var(--blue),var(--mint)); transition:width .5s cubic-bezier(.22,.8,.28,1)}
/* Layout */
.layout{display:grid; grid-template-columns:236px 1fr; gap:22px; align-items:start}
/* Stepper */
.stepper{position:sticky; top:20px; display:flex; flex-direction:column; gap:16px}
.stepper ol{list-style:none; margin:0; padding:0; display:flex; flex-direction:column; gap:4px}
.stepper li{display:flex; align-items:center; gap:12px; padding:11px 12px; border-radius:var(--r-md); cursor:pointer; transition:background .18s, transform .18s; color:var(--ink-2)}
.stepper li:hover{background:var(--white)}
.stepper li.is-active{background:var(--white); box-shadow:var(--sh-sm)}
.stepper li.is-active .stepper__num{background:var(--blue); color:#fff; border-color:var(--blue)}
.stepper li.is-done .stepper__num{background:var(--mint-50); color:var(--ok); border-color:rgba(34,176,125,.35)}
.stepper__num{width:30px; height:30px; flex:none; border-radius:9px; display:grid; place-items:center; font-weight:700; font-size:.9rem; background:#fff; border:1.5px solid var(--line-2); color:var(--muted); transition:.18s}
.stepper li.is-done .stepper__num::after{content:"\2713"}
.stepper li.is-done .stepper__num{font-size:0}
.stepper li.is-done .stepper__num::after{font-size:.95rem}
.stepper__label{display:flex; flex-direction:column; line-height:1.25}
.stepper__label strong{font-size:.9rem; font-weight:700; letter-spacing:-.01em; color:var(--ink)}
.stepper__label em{font-style:normal; font-size:.74rem; color:var(--muted)}
.review{background:var(--white); border:1px solid var(--line); border-radius:var(--r-md); padding:14px 15px; box-shadow:var(--sh-sm)}
.review__title{margin:0 0 10px; font-size:.72rem; text-transform:uppercase; letter-spacing:.08em; color:var(--muted); font-weight:700}
.review__row{display:flex; justify-content:space-between; gap:10px; font-size:.82rem; padding:5px 0; border-top:1px dashed var(--line)}
.review__row:first-of-type{border-top:none}
.review__row span{color:var(--muted)}
.review__row b{color:var(--ink); font-weight:600; text-align:right; max-width:60%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
/* Card */
.card{background:var(--white); border:1px solid var(--line); border-radius:var(--r-lg); box-shadow:var(--sh-md); padding:30px 32px; min-height:440px; position:relative}
.section{display:none; animation:fade .35s ease}
.section.is-active{display:block}
@keyframes fade{from{opacity:0; transform:translateY(8px)} to{opacity:1; transform:none}}
.section__head{margin-bottom:22px}
.section__head h2{margin:0 0 5px; font-size:1.45rem; font-weight:800; letter-spacing:-.02em}
.section__head p{margin:0; color:var(--muted); font-size:.92rem}
/* Fields */
.grid{display:grid; grid-template-columns:1fr 1fr; gap:16px}
.field{display:flex; flex-direction:column; gap:6px; min-width:0}
.field--wide{grid-column:1 / -1}
.field label{font-size:.84rem; font-weight:600; color:var(--ink-2)}
.field .opt{color:var(--muted); font-weight:500}
input,select,textarea{
font:inherit; color:var(--ink); background:#fbfdff;
border:1.5px solid var(--line-2); border-radius:var(--r-sm);
padding:11px 13px; width:100%; transition:border-color .16s, box-shadow .16s, background .16s;
}
textarea{resize:vertical; min-height:52px}
input::placeholder,textarea::placeholder{color:#9fb0c2}
input:hover,select:hover,textarea:hover{border-color:var(--blue)}
input:focus,select:focus,textarea:focus{outline:none; border-color:var(--blue); background:#fff; box-shadow:0 0 0 4px rgba(37,99,235,.14)}
.field.invalid input,.field.invalid select,.field.invalid textarea{border-color:var(--danger); background:#fff6f6}
.field.invalid input:focus,.field.invalid select:focus{box-shadow:0 0 0 4px rgba(224,82,82,.15)}
.err{color:var(--danger); font-size:.76rem; font-weight:500; min-height:0; opacity:0; transform:translateY(-3px); transition:.16s}
.field.invalid .err,#consentErr.show{opacity:1; transform:none; min-height:16px}
.field--reveal{animation:fade .3s ease}
/* Chips */
.chips{border:none; margin:0 0 18px; padding:0}
.chips legend{font-size:.84rem; font-weight:600; color:var(--ink-2); padding:0; margin-bottom:10px}
.chips__grid{display:grid; grid-template-columns:repeat(auto-fill,minmax(150px,1fr)); gap:10px}
.chip{
display:flex; align-items:center; gap:9px; padding:11px 13px; border-radius:var(--r-md);
border:1.5px solid var(--line-2); background:#fbfdff; font-size:.86rem; font-weight:500;
color:var(--ink-2); cursor:pointer; user-select:none; transition:.16s;
}
.chip:hover{border-color:var(--blue); background:var(--blue-50)}
.chip input{width:18px; height:18px; flex:none; accent-color:var(--blue); cursor:pointer; margin:0}
.chip:has(input:checked){border-color:var(--blue); background:var(--blue-50); color:var(--blue-d); font-weight:600; box-shadow:inset 0 0 0 1px var(--blue)}
.chip:active{transform:scale(.97)}
/* Insurance toggle */
.toggle{display:inline-flex; padding:4px; gap:4px; background:#f0f5fc; border:1px solid var(--line); border-radius:999px; margin-bottom:20px}
.toggle__opt{border:none; background:transparent; font:inherit; font-weight:600; font-size:.86rem; color:var(--ink-2); padding:9px 18px; border-radius:999px; cursor:pointer; transition:.18s}
.toggle__opt.is-on{background:#fff; color:var(--blue-d); box-shadow:var(--sh-sm)}
#insFields.hide{display:none}
.selfpay-note{background:var(--mint-50); border:1px solid rgba(34,176,125,.25); border-radius:var(--r-md); padding:14px 16px; font-size:.88rem; color:var(--ink-2)}
/* Summary + consent */
.summary{background:var(--blue-50); border:1px solid rgba(37,99,235,.18); border-radius:var(--r-md); padding:16px 18px; margin-bottom:20px}
.summary__row{display:flex; justify-content:space-between; gap:12px; font-size:.9rem; padding:7px 0; border-top:1px dashed rgba(37,99,235,.2)}
.summary__row:first-child{border-top:none}
.summary__row span{color:var(--ink-2)}
.summary__row b{color:var(--ink); font-weight:700; text-align:right}
.consent{display:flex; gap:12px; align-items:flex-start; padding:15px 16px; border:1.5px solid var(--line-2); border-radius:var(--r-md); background:#fbfdff; cursor:pointer; font-size:.88rem; color:var(--ink-2); transition:.16s}
.consent:hover{border-color:var(--blue)}
.consent:has(input:checked){border-color:var(--mint); background:var(--mint-50)}
.consent input{width:20px; height:20px; flex:none; accent-color:var(--ok); margin-top:1px; cursor:pointer}
.sign{font-style:italic; font-size:1.05rem}
/* Buttons */
.actions{display:flex; justify-content:space-between; align-items:center; gap:12px; margin-top:26px; padding-top:22px; border-top:1px solid var(--line)}
.btn{display:inline-flex; align-items:center; gap:8px; font:inherit; font-weight:700; font-size:.92rem; border:none; border-radius:999px; padding:12px 22px; cursor:pointer; transition:transform .14s, box-shadow .18s, background .18s}
.btn:focus-visible{outline:3px solid rgba(37,99,235,.35); outline-offset:2px}
.btn--primary{background:linear-gradient(150deg,var(--blue),var(--blue-d)); color:#fff; box-shadow:0 8px 20px rgba(37,99,235,.3)}
.btn--primary:hover{transform:translateY(-2px); box-shadow:0 12px 26px rgba(37,99,235,.38)}
.btn--primary:active{transform:translateY(0)}
.btn--submit{background:linear-gradient(150deg,var(--mint),var(--ok)); box-shadow:0 8px 20px rgba(34,176,125,.32)}
.btn--submit:hover{box-shadow:0 12px 26px rgba(34,176,125,.4)}
.btn--ghost{background:#fff; color:var(--ink-2); border:1.5px solid var(--line-2)}
.btn--ghost:hover{border-color:var(--blue); color:var(--blue-d); transform:translateY(-2px)}
/* Done */
.done{text-align:center; padding:34px 10px}
.done__icon{width:78px; height:78px; margin:0 auto 20px; border-radius:50%; display:grid; place-items:center; color:#fff; background:linear-gradient(150deg,var(--mint),var(--ok)); box-shadow:0 14px 34px rgba(34,176,125,.4); animation:pop .5s cubic-bezier(.2,1.3,.4,1)}
@keyframes pop{from{transform:scale(.4); opacity:0} to{transform:scale(1); opacity:1}}
.done h2{margin:0 0 8px; font-size:1.6rem}
.done p{color:var(--muted); max-width:360px; margin:0 auto 22px}
/* Toast */
.toast{
position:fixed; left:50%; bottom:28px; transform:translate(-50%,20px);
background:var(--ink); color:#fff; font-size:.88rem; font-weight:600;
padding:12px 20px; border-radius:999px; box-shadow:var(--sh-lg);
opacity:0; pointer-events:none; transition:.28s cubic-bezier(.2,.8,.3,1); z-index:50;
display:flex; align-items:center; gap:9px; max-width:90vw;
}
.toast.show{opacity:1; transform:translate(-50%,0)}
.toast--warn{background:var(--danger)}
.toast::before{content:""; width:8px; height:8px; border-radius:50%; background:var(--mint)}
.toast--warn::before{background:#fff}
/* Responsive */
@media (max-width:820px){
.layout{grid-template-columns:1fr}
.stepper{position:static; flex-direction:row; flex-wrap:wrap; gap:10px}
.stepper ol{flex-direction:row; flex-wrap:wrap; flex:1 1 100%}
.stepper li{flex:1 1 auto; min-width:120px}
.stepper__label em{display:none}
.review{flex:1 1 100%}
}
@media (max-width:520px){
.shell{padding:20px 14px 48px}
.card{padding:22px 18px; border-radius:var(--r-md)}
.grid{grid-template-columns:1fr}
.section__head h2{font-size:1.25rem}
.topbar__meta{gap:10px}
.save{display:none}
.stepper__label strong{font-size:.82rem}
.chips__grid{grid-template-columns:1fr 1fr}
.actions{flex-direction:row}
.btn{padding:11px 16px; font-size:.86rem}
}(function () {
"use strict";
var STEPS = ["personal", "history", "insurance", "consent"];
var current = 0;
var hasInsurance = true;
var form = document.getElementById("intakeForm");
var sections = {};
STEPS.concat(["done"]).forEach(function (s) {
sections[s] = form.querySelector('[data-section="' + s + '"]');
});
var stepItems = Array.prototype.slice.call(document.querySelectorAll("#stepList li"));
var progressFill = document.getElementById("progressFill");
var progressBar = document.querySelector(".progress");
var toastEl = document.getElementById("toast");
var toastTimer;
/* ---------- Toast ---------- */
function toast(msg, warn) {
toastEl.textContent = msg;
toastEl.className = "toast show" + (warn ? " toast--warn" : "");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.className = "toast" + (warn ? " toast--warn" : "");
}, 2600);
}
/* ---------- Validation ---------- */
function fieldWrap(el) {
return el.closest(".field");
}
function setError(el, msg) {
var wrap = fieldWrap(el);
if (!wrap) return;
var err = wrap.querySelector("[data-err]");
if (msg) {
wrap.classList.add("invalid");
if (err) err.textContent = msg;
} else {
wrap.classList.remove("invalid");
if (err) err.textContent = "";
}
}
function validateField(el) {
if (el.disabled || el.offsetParent === null) return true;
var v = (el.value || "").trim();
if (el.hasAttribute("required") && !v) {
setError(el, "This field is required");
return false;
}
if (el.type === "email" && v && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v)) {
setError(el, "Enter a valid email address");
return false;
}
if (el.type === "tel" && v && v.replace(/\D/g, "").length < 10) {
setError(el, "Enter a full phone number");
return false;
}
if (el.id === "memberId" && v && v.replace(/\D/g, "").length < 6) {
setError(el, "Member ID looks too short");
return false;
}
setError(el, "");
return true;
}
function sectionFields(step) {
var sec = sections[step];
return Array.prototype.slice.call(
sec.querySelectorAll("input, select, textarea")
).filter(function (el) {
return el.type !== "checkbox" && el.hasAttribute("required") && el.offsetParent !== null;
});
}
function validateSection(step) {
var ok = true;
var first = null;
sectionFields(step).forEach(function (el) {
if (!validateField(el)) {
ok = false;
if (!first) first = el;
}
});
// consent special-case
if (step === "consent") {
var consent = document.getElementById("consent");
var cErr = document.getElementById("consentErr");
if (!consent.checked) {
cErr.textContent = "Please provide consent to continue";
cErr.classList.add("show");
ok = false;
if (!first) first = consent;
} else {
cErr.classList.remove("show");
cErr.textContent = "";
}
}
if (first && first.focus) first.focus();
return ok;
}
/* ---------- Navigation ---------- */
function completion() {
// percentage across the 4 real steps
return Math.round(((current) / STEPS.length) * 100);
}
function setProgress(pct) {
progressFill.style.width = pct + "%";
progressBar.setAttribute("aria-valuenow", String(pct));
}
function show(index, opts) {
opts = opts || {};
var step = typeof index === "string" ? index : STEPS[index];
STEPS.concat(["done"]).forEach(function (s) {
sections[s].classList.toggle("is-active", s === step);
});
if (step === "done") {
setProgress(100);
stepItems.forEach(function (li) {
li.classList.remove("is-active");
li.classList.add("is-done");
});
return;
}
current = STEPS.indexOf(step);
stepItems.forEach(function (li, i) {
li.classList.toggle("is-active", i === current);
li.classList.toggle("is-done", i < current);
});
setProgress(Math.round((current / STEPS.length) * 100));
if (!opts.silent) {
form.scrollIntoView({ behavior: "smooth", block: "start" });
}
}
function goNext() {
var step = STEPS[current];
if (!validateSection(step)) {
toast("Please fix the highlighted fields", true);
return;
}
if (current < STEPS.length - 1) {
show(current + 1);
updateReview();
var labels = ["", "History next — almost there", "Nice — now your coverage", "Last step: review & sign"];
toast(labels[current] || "Saved");
}
}
function goPrev() {
if (current > 0) show(current - 1);
}
form.querySelectorAll("[data-next]").forEach(function (b) {
b.addEventListener("click", goNext);
});
form.querySelectorAll("[data-prev]").forEach(function (b) {
b.addEventListener("click", goPrev);
});
// Stepper clicks — only allow going to visited/next
stepItems.forEach(function (li, i) {
li.addEventListener("click", function () {
if (i <= current) {
show(i);
} else if (i === current + 1) {
goNext();
} else {
toast("Finish the current section first", true);
}
});
});
/* ---------- Live validation on blur ---------- */
form.addEventListener(
"blur",
function (e) {
var el = e.target;
if (el.matches("input, select, textarea") && el.hasAttribute("required")) {
validateField(el);
}
},
true
);
form.addEventListener("input", function (e) {
var el = e.target;
if (el.closest(".field.invalid")) validateField(el);
});
/* ---------- Phone formatting ---------- */
var phone = document.getElementById("phone");
phone.addEventListener("input", function () {
var d = phone.value.replace(/\D/g, "").slice(0, 10);
var out = d;
if (d.length > 6) out = "(" + d.slice(0, 3) + ") " + d.slice(3, 6) + "-" + d.slice(6);
else if (d.length > 3) out = "(" + d.slice(0, 3) + ") " + d.slice(3);
else if (d.length > 0) out = "(" + d;
phone.value = out;
});
/* ---------- Member ID formatting ---------- */
var memberId = document.getElementById("memberId");
memberId.addEventListener("input", function () {
var d = memberId.value.replace(/\D/g, "").slice(0, 12);
memberId.value = d.replace(/(.{4})/g, "$1 ").trim();
});
/* ---------- Condition chips: "None" is exclusive ---------- */
var condWrap = document.getElementById("conditionChips");
condWrap.addEventListener("change", function (e) {
var box = e.target;
if (box.dataset.none !== undefined && box.checked) {
condWrap.querySelectorAll('input[type="checkbox"]').forEach(function (c) {
if (c !== box) c.checked = false;
});
} else if (box.checked) {
var none = condWrap.querySelector("[data-none]");
if (none) none.checked = false;
}
});
/* ---------- Allergy "Other" reveals note ---------- */
var allergyWrap = document.getElementById("allergyChips");
var noteWrap = document.getElementById("allergyNoteWrap");
var noteInput = document.getElementById("allergyNote");
allergyWrap.addEventListener("change", function (e) {
if (e.target.dataset.other === undefined) return;
var on = e.target.checked;
noteWrap.hidden = !on;
noteInput.toggleAttribute("required", on);
if (on) setTimeout(function () { noteInput.focus(); }, 60);
else setError(noteInput, "");
});
/* ---------- Insurance toggle ---------- */
var insFields = document.getElementById("insFields");
var insBtns = document.querySelectorAll(".toggle__opt");
var selfpayNote = null;
insBtns.forEach(function (btn) {
btn.addEventListener("click", function () {
hasInsurance = btn.dataset.ins === "yes";
insBtns.forEach(function (b) {
var on = b === btn;
b.classList.toggle("is-on", on);
b.setAttribute("aria-pressed", String(on));
});
insFields.classList.toggle("hide", !hasInsurance);
insFields.querySelectorAll("[required]").forEach(function (el) {
// keep original required intent only when visible
});
// toggle required on carrier/memberId/holder
["carrier", "memberId", "holder"].forEach(function (id) {
var el = document.getElementById(id);
if (hasInsurance) el.setAttribute("required", "");
else { el.removeAttribute("required"); setError(el, ""); }
});
if (!hasInsurance && !selfpayNote) {
selfpayNote = document.createElement("div");
selfpayNote.className = "selfpay-note";
selfpayNote.textContent = "No problem — we'll review self-pay options and payment plans with you at check-in.";
insFields.after(selfpayNote);
}
if (selfpayNote) selfpayNote.style.display = hasInsurance ? "none" : "block";
updateReview();
});
});
/* ---------- Review + summary ---------- */
function val(id) {
var el = document.getElementById(id);
return el && el.value ? el.value.trim() : "";
}
function updateReview() {
var name = [val("firstName"), val("lastName")].filter(Boolean).join(" ") || "—";
var reason = val("reason") || "—";
var ins = hasInsurance ? (val("carrier") || "Insured") : "Self-pay";
document.getElementById("rvName").textContent = name;
document.getElementById("rvReason").textContent = reason;
document.getElementById("rvIns").textContent = ins;
document.getElementById("sumName").textContent = name;
document.getElementById("sumReason").textContent = reason;
document.getElementById("sumVisit").textContent = val("lastVisit") || "—";
document.getElementById("sumIns").textContent = ins === "—" ? "—" : ins;
}
form.addEventListener("input", updateReview);
form.addEventListener("change", updateReview);
/* ---------- Submit ---------- */
form.addEventListener("submit", function (e) {
e.preventDefault();
if (!validateSection("consent")) {
toast("Please review the highlighted items", true);
return;
}
var name = val("firstName") || "friend";
document.getElementById("doneName").textContent = name;
show("done");
toast("Intake submitted — thank you!");
});
document.getElementById("restartBtn").addEventListener("click", function () {
form.reset();
noteWrap.hidden = true;
noteInput.removeAttribute("required");
form.querySelectorAll(".field.invalid").forEach(function (f) { f.classList.remove("invalid"); });
document.getElementById("consentErr").classList.remove("show");
hasInsurance = true;
insBtns[0].click();
updateReview();
show(0);
toast("Fresh form ready");
});
// init
updateReview();
setProgress(0);
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Brightleaf Dental — New Patient Intake</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="shell">
<header class="topbar">
<div class="brand">
<div class="brand__mark" aria-hidden="true">
<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 5.5c-2-2-5-2-6.5-.5C3.5 7 4 10 5 13c.8 2.4 1 5.5 2.5 5.5 1.2 0 1.3-2.5 2-4 .3-.6.7-1 2.5-1s2.2.4 2.5 1c.7 1.5.8 4 2 4 1.5 0 1.7-3.1 2.5-5.5 1-3 1.5-6-.5-8-1.5-1.5-4.5-1.5-6.5.5Z"/>
</svg>
</div>
<div class="brand__txt">
<strong>Brightleaf Dental</strong>
<span>New Patient Intake</span>
</div>
</div>
<div class="topbar__meta">
<span class="pill pill--ok"><span class="dot"></span>Secure & confidential</span>
<span class="save">Draft saved automatically</span>
</div>
</header>
<div class="progress" role="progressbar" aria-label="Form completion" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
<div class="progress__fill" id="progressFill"></div>
</div>
<main class="layout">
<aside class="stepper" aria-label="Sections">
<ol id="stepList">
<li data-step="personal" class="is-active">
<span class="stepper__num">1</span>
<span class="stepper__label"><strong>Personal</strong><em>About you</em></span>
</li>
<li data-step="history">
<span class="stepper__num">2</span>
<span class="stepper__label"><strong>History</strong><em>Medical & dental</em></span>
</li>
<li data-step="insurance">
<span class="stepper__num">3</span>
<span class="stepper__label"><strong>Insurance</strong><em>Coverage</em></span>
</li>
<li data-step="consent">
<span class="stepper__num">4</span>
<span class="stepper__label"><strong>Consent</strong><em>Sign & submit</em></span>
</li>
</ol>
<div class="review" aria-live="polite">
<p class="review__title">Review</p>
<div class="review__row"><span>Patient</span><b id="rvName">—</b></div>
<div class="review__row"><span>Reason</span><b id="rvReason">—</b></div>
<div class="review__row"><span>Insurance</span><b id="rvIns">—</b></div>
</div>
</aside>
<form class="card" id="intakeForm" novalidate>
<!-- Section 1: Personal -->
<section class="section is-active" data-section="personal">
<div class="section__head">
<h2>Tell us about you</h2>
<p>We use this to set up your chart and confirm appointments.</p>
</div>
<div class="grid">
<div class="field">
<label for="firstName">First name</label>
<input id="firstName" name="firstName" type="text" autocomplete="given-name" required />
<small class="err" data-err></small>
</div>
<div class="field">
<label for="lastName">Last name</label>
<input id="lastName" name="lastName" type="text" autocomplete="family-name" required />
<small class="err" data-err></small>
</div>
<div class="field">
<label for="dob">Date of birth</label>
<input id="dob" name="dob" type="date" required />
<small class="err" data-err></small>
</div>
<div class="field">
<label for="phone">Mobile phone</label>
<input id="phone" name="phone" type="tel" inputmode="tel" placeholder="(555) 123-4567" required />
<small class="err" data-err></small>
</div>
<div class="field field--wide">
<label for="email">Email</label>
<input id="email" name="email" type="email" autocomplete="email" placeholder="[email protected]" required />
<small class="err" data-err></small>
</div>
<div class="field field--wide">
<label for="reason">Reason for visit</label>
<select id="reason" name="reason" required>
<option value="" disabled selected>Choose one…</option>
<option>New patient checkup & cleaning</option>
<option>Tooth pain or sensitivity</option>
<option>Cosmetic consultation</option>
<option>Emergency / broken tooth</option>
<option>Second opinion</option>
</select>
<small class="err" data-err></small>
</div>
</div>
<div class="actions">
<span></span>
<button type="button" class="btn btn--primary" data-next>Continue<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg></button>
</div>
</section>
<!-- Section 2: History -->
<section class="section" data-section="history">
<div class="section__head">
<h2>Medical & dental history</h2>
<p>Check anything that applies. This keeps your care safe.</p>
</div>
<fieldset class="chips">
<legend>Do any of these apply to you?</legend>
<div class="chips__grid" id="conditionChips">
<label class="chip"><input type="checkbox" name="conditions" value="Heart condition" />Heart condition</label>
<label class="chip"><input type="checkbox" name="conditions" value="High blood pressure" />High blood pressure</label>
<label class="chip"><input type="checkbox" name="conditions" value="Diabetes" />Diabetes</label>
<label class="chip"><input type="checkbox" name="conditions" value="Asthma" />Asthma</label>
<label class="chip"><input type="checkbox" name="conditions" value="Pregnant" />Pregnant</label>
<label class="chip"><input type="checkbox" name="conditions" value="Blood thinners" />On blood thinners</label>
<label class="chip"><input type="checkbox" name="conditions" value="None" data-none>None of these</label>
</div>
</fieldset>
<fieldset class="chips">
<legend>Any allergies?</legend>
<div class="chips__grid" id="allergyChips">
<label class="chip"><input type="checkbox" name="allergies" value="Latex" />Latex</label>
<label class="chip"><input type="checkbox" name="allergies" value="Penicillin" />Penicillin</label>
<label class="chip"><input type="checkbox" name="allergies" value="Local anesthetic" />Local anesthetic</label>
<label class="chip"><input type="checkbox" name="allergies" value="Aspirin" />Aspirin</label>
<label class="chip"><input type="checkbox" name="allergies" value="Other" data-other>Other</label>
</div>
</fieldset>
<div class="field field--reveal" id="allergyNoteWrap" hidden>
<label for="allergyNote">Please describe the allergy</label>
<input id="allergyNote" name="allergyNote" type="text" placeholder="e.g. hives from amoxicillin" />
<small class="err" data-err></small>
</div>
<div class="grid">
<div class="field">
<label for="lastVisit">Last dental visit</label>
<select id="lastVisit" name="lastVisit" required>
<option value="" disabled selected>Choose one…</option>
<option>Within 6 months</option>
<option>6–12 months ago</option>
<option>1–2 years ago</option>
<option>More than 2 years</option>
<option>Can't remember</option>
</select>
<small class="err" data-err></small>
</div>
<div class="field">
<label for="anxiety">Dental anxiety</label>
<select id="anxiety" name="anxiety" required>
<option value="" disabled selected>Choose one…</option>
<option>None — I'm comfortable</option>
<option>A little nervous</option>
<option>Quite anxious</option>
<option>Very anxious</option>
</select>
<small class="err" data-err></small>
</div>
<div class="field field--wide">
<label for="meds">Current medications <span class="opt">(optional)</span></label>
<textarea id="meds" name="meds" rows="2" placeholder="List any medications you take regularly"></textarea>
</div>
</div>
<div class="actions">
<button type="button" class="btn btn--ghost" data-prev>Back</button>
<button type="button" class="btn btn--primary" data-next>Continue<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg></button>
</div>
</section>
<!-- Section 3: Insurance -->
<section class="section" data-section="insurance">
<div class="section__head">
<h2>Insurance & billing</h2>
<p>We'll verify your benefits before your visit.</p>
</div>
<div class="toggle" role="radiogroup" aria-label="Do you have dental insurance?">
<button type="button" class="toggle__opt is-on" data-ins="yes" aria-pressed="true">I have insurance</button>
<button type="button" class="toggle__opt" data-ins="no" aria-pressed="false">Self-pay</button>
</div>
<div class="grid" id="insFields">
<div class="field">
<label for="carrier">Insurance carrier</label>
<select id="carrier" name="carrier" required>
<option value="" disabled selected>Choose one…</option>
<option>Delta Dental</option>
<option>Cigna</option>
<option>MetLife</option>
<option>Aetna</option>
<option>Guardian</option>
<option>Other</option>
</select>
<small class="err" data-err></small>
</div>
<div class="field">
<label for="memberId">Member ID</label>
<input id="memberId" name="memberId" type="text" inputmode="numeric" placeholder="0000 0000 0000" required />
<small class="err" data-err></small>
</div>
<div class="field">
<label for="holder">Policy holder</label>
<select id="holder" name="holder" required>
<option value="" disabled selected>Choose one…</option>
<option>Myself</option>
<option>Spouse / partner</option>
<option>Parent / guardian</option>
</select>
<small class="err" data-err></small>
</div>
<div class="field">
<label for="group">Group number <span class="opt">(optional)</span></label>
<input id="group" name="group" type="text" placeholder="e.g. GRP-4821" />
</div>
</div>
<div class="actions">
<button type="button" class="btn btn--ghost" data-prev>Back</button>
<button type="button" class="btn btn--primary" data-next>Continue<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14M13 6l6 6-6 6"/></svg></button>
</div>
</section>
<!-- Section 4: Consent -->
<section class="section" data-section="consent">
<div class="section__head">
<h2>Consent & signature</h2>
<p>One last step and you're all set for your visit.</p>
</div>
<div class="summary" id="summaryCard">
<div class="summary__row"><span>Patient</span><b id="sumName">—</b></div>
<div class="summary__row"><span>Reason</span><b id="sumReason">—</b></div>
<div class="summary__row"><span>Last visit</span><b id="sumVisit">—</b></div>
<div class="summary__row"><span>Insurance</span><b id="sumIns">—</b></div>
</div>
<label class="consent">
<input type="checkbox" id="consent" name="consent" required />
<span>I confirm the information above is accurate and I consent to a dental examination and treatment plan at Brightleaf Dental. I understand this can be updated at any time.</span>
</label>
<small class="err" data-err id="consentErr"></small>
<div class="field field--wide">
<label for="signature">Type your full name to sign</label>
<input id="signature" name="signature" type="text" class="sign" placeholder="Full legal name" required />
<small class="err" data-err></small>
</div>
<div class="actions">
<button type="button" class="btn btn--ghost" data-prev>Back</button>
<button type="submit" class="btn btn--primary btn--submit">Submit intake<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg></button>
</div>
</section>
<!-- Success -->
<section class="section done" data-section="done">
<div class="done__icon" aria-hidden="true">
<svg viewBox="0 0 24 24" width="34" height="34" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>
</div>
<h2>You're all set, <span id="doneName">friend</span>!</h2>
<p>Your intake was received. Our front desk will confirm your benefits and see you soon.</p>
<button type="button" class="btn btn--ghost" id="restartBtn">Start a new intake</button>
</section>
</form>
</main>
</div>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>A trust-first intake experience for a fictional dental clinic, Brightleaf Dental. The form is split into four reassuring sections — Personal, Medical & Dental History, Insurance, and Consent — with a sticky progress bar and a side stepper that shows which sections are complete. Every required field validates inline as the patient moves through the form, so nothing is a surprise at the end.
The medical history section uses a grid of soft checkbox chips for conditions and allergies, and reveals a follow-up note field when the patient marks something that needs detail. Insurance fields format the member ID as you type, and a live-updating review card echoes the patient’s name and appointment reason so they feel seen. Toast messages confirm progress and a final signature line gates submission behind an explicit consent checkbox.
Micro-interactions throughout — chip presses, focus rings, a smooth animated progress fill, and a success state — keep the flow calm and clinical rather than clerical. Everything is keyboard-navigable with visible focus and works down to a 360px viewport.
Illustrative UI only — not intended for real medical use.