Clinic — Referral / Consult Request
A referral and consult request form for sending a patient to a specialist. A read-only patient chip anchors the request, a specialty select and segmented Routine / Urgent / Emergent urgency control set its scope, and twin textareas capture the reason and relevant history. An Attach results checklist toggles Labs, Imaging and Notes while a live preview card mirrors every field, then submit validates required fields and flips to a confirmation state with a generated reference number and toast.
MCP
コード
:root {
--teal: #129c93;
--teal-d: #0c7a73;
--teal-700: #0a655f;
--teal-50: #e7f5f3;
--coral: #ff7a66;
--coral-soft: #ffe6df;
--ink: #16322f;
--ink-2: #3a534f;
--muted: #6b827e;
--bg: #f1f7f6;
--white: #ffffff;
--line: rgba(16, 50, 47, 0.1);
--line-2: rgba(16, 50, 47, 0.18);
--ok: #2f9e6f;
--warn: #d98a2b;
--danger: #d4503e;
--font: "Inter", system-ui, -apple-system, sans-serif;
--r-sm: 8px;
--r-md: 14px;
--r-lg: 20px;
--shadow-1: 0 1px 2px rgba(16, 50, 47, 0.05), 0 4px 14px rgba(16, 50, 47, 0.06);
--shadow-2: 0 16px 40px rgba(12, 122, 115, 0.16);
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: var(--font);
background: var(--bg);
color: var(--ink);
-webkit-font-smoothing: antialiased;
line-height: 1.5;
}
:focus-visible {
outline: 2px solid var(--teal);
outline-offset: 2px;
border-radius: 4px;
}
/* ── Layout ── */
.ref {
max-width: 1040px;
margin: 0 auto;
padding: 36px 20px 64px;
display: grid;
grid-template-columns: 1fr 360px;
gap: 28px;
align-items: start;
}
/* ── Form card ── */
.ref-form {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-lg);
box-shadow: var(--shadow-1);
padding: 26px;
display: flex;
flex-direction: column;
gap: 22px;
}
.ref-head {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 16px;
padding-bottom: 20px;
border-bottom: 1px solid var(--line);
}
.eyebrow {
font-size: 0.74rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--teal-d);
}
.ref-head h1 {
font-size: 1.42rem;
font-weight: 800;
letter-spacing: -0.02em;
margin-top: 4px;
}
.sub {
font-size: 0.88rem;
color: var(--muted);
margin-top: 6px;
max-width: 46ch;
}
.draft-pill {
flex-shrink: 0;
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.03em;
padding: 5px 12px;
border-radius: 999px;
background: var(--teal-50);
color: var(--teal-d);
transition: background 0.2s, color 0.2s;
}
.draft-pill.is-ready {
background: rgba(47, 158, 111, 0.16);
color: var(--ok);
}
/* ── Fields ── */
.field {
display: flex;
flex-direction: column;
gap: 9px;
}
.lbl {
font-size: 0.86rem;
font-weight: 700;
color: var(--ink);
}
.lbl-row {
display: flex;
align-items: baseline;
justify-content: space-between;
}
.req {
color: var(--danger);
font-weight: 800;
}
.counter {
font-size: 0.74rem;
font-weight: 600;
color: var(--muted);
font-variant-numeric: tabular-nums;
}
.counter.near {
color: var(--warn);
}
.hint {
font-size: 0.78rem;
color: var(--muted);
}
/* ── Patient chip ── */
.patient-chip {
display: flex;
align-items: center;
gap: 13px;
background: linear-gradient(140deg, var(--teal-50), #d9efec);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 12px 14px;
}
.avatar {
flex-shrink: 0;
width: 42px;
height: 42px;
border-radius: 50%;
background: var(--teal-d);
color: #fff;
display: grid;
place-items: center;
font-size: 0.86rem;
font-weight: 800;
letter-spacing: 0.02em;
}
.avatar.sm {
width: 34px;
height: 34px;
font-size: 0.72rem;
}
.patient-meta {
display: flex;
flex-direction: column;
min-width: 0;
flex: 1;
}
.patient-meta strong {
font-size: 0.96rem;
font-weight: 700;
}
.patient-meta span {
font-size: 0.78rem;
color: var(--ink-2);
}
.chip-tag {
flex-shrink: 0;
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 0.7rem;
font-weight: 700;
color: var(--teal-700);
background: rgba(255, 255, 255, 0.7);
border-radius: 999px;
padding: 4px 9px;
}
/* ── Select ── */
.select-wrap {
position: relative;
}
select {
width: 100%;
appearance: none;
font: inherit;
font-size: 0.9rem;
font-weight: 500;
color: var(--ink);
background: var(--white);
border: 1px solid var(--line-2);
border-radius: var(--r-sm);
padding: 12px 40px 12px 13px;
cursor: pointer;
transition: border-color 0.15s, box-shadow 0.15s;
}
select:hover {
border-color: var(--teal);
}
select:focus {
outline: none;
border-color: var(--teal);
box-shadow: 0 0 0 3px rgba(18, 156, 147, 0.16);
}
select:invalid {
color: var(--muted);
}
.caret {
position: absolute;
right: 12px;
top: 50%;
transform: translateY(-50%);
width: 20px;
height: 20px;
color: var(--muted);
pointer-events: none;
}
/* ── Segmented urgency ── */
.seg {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 4px;
background: var(--bg);
border: 1px solid var(--line);
border-radius: 999px;
padding: 4px;
}
.seg-btn {
border: none;
background: transparent;
border-radius: 999px;
padding: 9px 10px;
font: inherit;
font-weight: 700;
font-size: 0.84rem;
color: var(--muted);
cursor: pointer;
transition: background 0.16s, color 0.16s, box-shadow 0.16s;
}
.seg-btn:hover {
color: var(--ink-2);
}
.seg-btn.is-active {
background: var(--white);
box-shadow: var(--shadow-1);
}
.seg-btn.is-active[data-tone="ok"] {
color: var(--ok);
}
.seg-btn.is-active[data-tone="warn"] {
color: var(--warn);
}
.seg-btn.is-active[data-tone="danger"] {
color: var(--danger);
}
/* ── Textareas ── */
textarea {
width: 100%;
font: inherit;
font-size: 0.9rem;
color: var(--ink);
background: var(--white);
border: 1px solid var(--line-2);
border-radius: var(--r-sm);
padding: 12px 13px;
resize: vertical;
min-height: 80px;
transition: border-color 0.15s, box-shadow 0.15s;
}
textarea::placeholder {
color: var(--muted);
}
textarea:hover {
border-color: var(--teal);
}
textarea:focus {
outline: none;
border-color: var(--teal);
box-shadow: 0 0 0 3px rgba(18, 156, 147, 0.16);
}
/* ── Validation ── */
.err {
font-size: 0.78rem;
font-weight: 600;
color: var(--danger);
}
.field.invalid select,
.field.invalid textarea {
border-color: var(--danger);
box-shadow: 0 0 0 3px rgba(212, 80, 62, 0.12);
}
/* ── Attach checklist ── */
.check-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.check {
display: flex;
align-items: flex-start;
gap: 12px;
border: 1px solid var(--line-2);
border-radius: var(--r-sm);
padding: 12px 14px;
cursor: pointer;
transition: border-color 0.15s, background 0.15s;
}
.check:hover {
border-color: var(--teal);
background: var(--teal-50);
}
.check input {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.box {
flex-shrink: 0;
width: 20px;
height: 20px;
margin-top: 1px;
border: 2px solid var(--line-2);
border-radius: 6px;
background: var(--white);
display: grid;
place-items: center;
transition: background 0.15s, border-color 0.15s;
}
.box::after {
content: "";
width: 10px;
height: 6px;
border-left: 2.4px solid #fff;
border-bottom: 2.4px solid #fff;
transform: rotate(-45deg) scale(0);
transform-origin: center;
transition: transform 0.15s ease;
margin-top: -2px;
}
.check input:checked ~ .box {
background: var(--teal-d);
border-color: var(--teal-d);
}
.check input:checked ~ .box::after {
transform: rotate(-45deg) scale(1);
}
.check:has(input:checked) {
border-color: var(--teal);
background: var(--teal-50);
}
.check input:focus-visible ~ .box {
outline: 2px solid var(--teal);
outline-offset: 2px;
}
.check-body {
display: flex;
flex-direction: column;
}
.check-body strong {
font-size: 0.88rem;
font-weight: 700;
}
.check-body small {
font-size: 0.76rem;
color: var(--muted);
}
/* ── Form actions ── */
.ref-actions {
display: flex;
justify-content: flex-end;
gap: 10px;
padding-top: 8px;
border-top: 1px solid var(--line);
margin-top: 2px;
padding-top: 20px;
}
.btn {
border: none;
border-radius: 10px;
padding: 11px 20px;
font: inherit;
font-weight: 700;
font-size: 0.88rem;
cursor: pointer;
transition: transform 0.12s, background 0.15s, border-color 0.15s,
box-shadow 0.15s;
}
.btn:active {
transform: translateY(1px);
}
.btn.full {
width: 100%;
}
.btn.ghost {
background: var(--white);
border: 1px solid var(--line-2);
color: var(--ink-2);
}
.btn.ghost:hover {
background: var(--teal-50);
border-color: var(--teal);
color: var(--teal-d);
}
.btn.solid {
background: var(--teal-d);
color: #fff;
box-shadow: 0 6px 16px rgba(12, 122, 115, 0.22);
}
.btn.solid:hover {
background: var(--teal-700);
}
/* ── Preview column ── */
.preview {
position: sticky;
top: 24px;
}
.preview-card {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-lg);
box-shadow: var(--shadow-1);
padding: 22px;
display: flex;
flex-direction: column;
gap: 16px;
}
.preview-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
}
.brand {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: 0.84rem;
font-weight: 800;
color: var(--ink);
letter-spacing: -0.01em;
}
.brand-dot {
width: 11px;
height: 11px;
border-radius: 50%;
background: var(--teal);
box-shadow: 0 0 0 4px var(--teal-50);
}
.badge.urg {
font-size: 0.72rem;
font-weight: 700;
padding: 4px 11px;
border-radius: 999px;
white-space: nowrap;
}
.badge.urg.ok {
background: rgba(47, 158, 111, 0.14);
color: var(--ok);
}
.badge.urg.warn {
background: rgba(217, 138, 43, 0.16);
color: var(--warn);
}
.badge.urg.danger {
background: rgba(212, 80, 62, 0.14);
color: var(--danger);
}
.pv-kicker {
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--muted);
margin-top: -6px;
}
.pv-patient {
display: flex;
align-items: center;
gap: 11px;
padding: 12px;
background: var(--bg);
border-radius: var(--r-md);
}
.pv-patient strong {
display: block;
font-size: 0.9rem;
font-weight: 700;
}
.pv-patient span {
font-size: 0.76rem;
color: var(--muted);
}
.pv-grid {
display: flex;
flex-direction: column;
gap: 12px;
}
.pv-row dt,
.pv-block dt {
font-size: 0.72rem;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--muted);
margin-bottom: 3px;
}
.pv-row dd {
font-size: 0.88rem;
font-weight: 600;
color: var(--ink);
}
.pv-block {
border-top: 1px dashed var(--line);
padding-top: 13px;
}
.pv-block dd {
font-size: 0.84rem;
color: var(--ink-2);
white-space: pre-wrap;
word-break: break-word;
}
.empty {
color: var(--muted) !important;
font-weight: 500 !important;
font-style: italic;
}
.pv-tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.pv-tag {
font-size: 0.72rem;
font-weight: 700;
color: var(--teal-d);
background: var(--teal-50);
border-radius: 999px;
padding: 4px 10px;
}
.pv-foot {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
border-top: 1px solid var(--line);
padding-top: 13px;
font-size: 0.74rem;
color: var(--muted);
font-weight: 600;
}
.pv-ref {
font-variant-numeric: tabular-nums;
letter-spacing: 0.02em;
}
/* ── Confirmation ── */
.confirm-card {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-lg);
box-shadow: var(--shadow-2);
padding: 26px 22px;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
gap: 14px;
animation: pop 0.35s cubic-bezier(0.2, 0.9, 0.3, 1.2);
}
@keyframes pop {
from {
opacity: 0;
transform: translateY(10px) scale(0.97);
}
to {
opacity: 1;
transform: none;
}
}
.confirm-mark {
width: 56px;
height: 56px;
border-radius: 50%;
background: rgba(47, 158, 111, 0.14);
color: var(--ok);
display: grid;
place-items: center;
}
.confirm-card h2 {
font-size: 1.2rem;
font-weight: 800;
letter-spacing: -0.02em;
}
.confirm-sub {
font-size: 0.84rem;
color: var(--muted);
max-width: 32ch;
}
.ref-number {
width: 100%;
background: var(--teal-50);
border: 1px dashed var(--teal);
border-radius: var(--r-md);
padding: 13px;
display: flex;
flex-direction: column;
gap: 2px;
}
.ref-label {
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.05em;
text-transform: uppercase;
color: var(--teal-d);
}
.ref-number strong {
font-size: 1.24rem;
font-weight: 800;
color: var(--teal-700);
letter-spacing: 0.02em;
font-variant-numeric: tabular-nums;
}
.confirm-list {
width: 100%;
list-style: none;
display: flex;
flex-direction: column;
gap: 0;
}
.confirm-list li {
display: flex;
justify-content: space-between;
gap: 12px;
padding: 9px 2px;
font-size: 0.84rem;
font-weight: 600;
border-top: 1px solid var(--line);
text-align: left;
}
.confirm-list .k {
color: var(--muted);
font-weight: 600;
}
/* ── Toast ── */
.toast {
position: fixed;
left: 50%;
bottom: 26px;
transform: translateX(-50%) translateY(8px);
background: var(--ink);
color: #fff;
padding: 13px 20px;
border-radius: 12px;
font-size: 0.9rem;
font-weight: 600;
box-shadow: var(--shadow-2);
z-index: 50;
max-width: 90vw;
opacity: 0;
transition: opacity 0.22s, transform 0.22s;
}
.toast.show {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
.toast::before {
content: "✓";
margin-right: 8px;
color: #6fe0b4;
font-weight: 800;
}
/* ── Responsive ── */
@media (max-width: 860px) {
.ref {
grid-template-columns: 1fr;
}
.preview {
position: static;
order: -1;
}
}
@media (max-width: 520px) {
.ref {
padding: 22px 14px 48px;
}
.ref-form {
padding: 20px 16px;
}
.ref-head {
flex-direction: column;
gap: 12px;
}
.seg-btn {
padding: 9px 4px;
font-size: 0.8rem;
}
.ref-actions {
flex-direction: column-reverse;
}
.ref-actions .btn {
width: 100%;
}
.patient-chip {
flex-wrap: wrap;
}
}
/* Visibility guard: honor the [hidden] attribute over base display */
.confirm-card[hidden] {
display: none;
}(function () {
"use strict";
const form = document.getElementById("referral-form");
const specialty = document.getElementById("specialty");
const reason = document.getElementById("reason");
const history = document.getElementById("history");
const urgency = document.getElementById("urgency");
const draftPill = document.getElementById("draft-pill");
// Preview targets
const pvUrgency = document.getElementById("pv-urgency");
const pvSpecialty = document.getElementById("pv-specialty");
const pvReason = document.getElementById("pv-reason");
const pvHistory = document.getElementById("pv-history");
const pvAttach = document.getElementById("pv-attach");
const pvDate = document.getElementById("pv-date");
const pvRef = document.getElementById("pv-ref");
// Confirmation
const previewCard = document.getElementById("preview-card");
const confirmCard = document.getElementById("confirm-card");
const confirmRef = document.getElementById("confirm-ref");
const cfSpec = document.getElementById("cf-spec");
const cfUrg = document.getElementById("cf-urg");
const cfAttach = document.getElementById("cf-attach");
const newBtn = document.getElementById("new-btn");
const urgencyHint = document.getElementById("urgency-hint");
let currentUrgency = "Routine";
/* ── Toast helper ── */
let toastTimer;
const toastEl = document.getElementById("toast");
function toast(msg) {
toastEl.textContent = msg;
toastEl.hidden = false;
requestAnimationFrame(() => toastEl.classList.add("show"));
clearTimeout(toastTimer);
toastTimer = setTimeout(() => {
toastEl.classList.remove("show");
setTimeout(() => (toastEl.hidden = true), 240);
}, 2600);
}
/* ── Date helper ── */
function today() {
return new Date().toLocaleDateString("en-US", {
day: "2-digit",
month: "short",
year: "numeric",
});
}
/* ── Character counters ── */
function bindCounter(el, counterId, max) {
const counter = document.getElementById(counterId);
function update() {
const len = el.value.length;
counter.textContent = len + " / " + max;
counter.classList.toggle("near", len >= max * 0.9);
}
el.addEventListener("input", update);
update();
}
bindCounter(reason, "count-reason", 400);
bindCounter(history, "count-history", 600);
/* ── Urgency segmented control ── */
const segBtns = Array.from(urgency.querySelectorAll(".seg-btn"));
const HINTS = {
Routine: "Routine consults are typically scheduled within 2–4 weeks.",
Urgent: "Urgent consults are reviewed by the receiving team within 48 hours.",
Emergent:
"Emergent requests trigger an immediate call to the on-call specialist.",
};
const TONE = { Routine: "ok", Urgent: "warn", Emergent: "danger" };
function setUrgency(btn) {
segBtns.forEach((b) => {
const on = b === btn;
b.classList.toggle("is-active", on);
b.setAttribute("aria-checked", String(on));
});
currentUrgency = btn.dataset.value;
urgencyHint.textContent = HINTS[currentUrgency];
updatePreview();
}
segBtns.forEach((btn, i) => {
btn.addEventListener("click", () => setUrgency(btn));
btn.addEventListener("keydown", (e) => {
let next = null;
if (e.key === "ArrowRight" || e.key === "ArrowDown") {
next = segBtns[(i + 1) % segBtns.length];
} else if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
next = segBtns[(i - 1 + segBtns.length) % segBtns.length];
}
if (next) {
e.preventDefault();
setUrgency(next);
next.focus();
}
});
});
/* ── Attachments ── */
function selectedAttachments() {
return Array.from(
form.querySelectorAll('input[name="attach"]:checked')
).map((c) => c.value);
}
/* ── Live preview ── */
function setField(el, value, emptyText) {
if (value && value.trim()) {
el.textContent = value.trim();
el.classList.remove("empty");
} else {
el.textContent = emptyText;
el.classList.add("empty");
}
}
let ready = false;
function updatePreview() {
// Urgency badge
pvUrgency.textContent = currentUrgency;
pvUrgency.className = "badge urg " + TONE[currentUrgency];
setField(pvSpecialty, specialty.value, "Not selected");
setField(pvReason, reason.value, "—");
setField(pvHistory, history.value, "—");
// Attachments as tags
const attach = selectedAttachments();
if (attach.length) {
pvAttach.classList.remove("empty");
pvAttach.innerHTML =
'<span class="pv-tags">' +
attach.map((a) => '<span class="pv-tag">' + a + "</span>").join("") +
"</span>";
} else {
pvAttach.classList.add("empty");
pvAttach.textContent = "None selected";
}
pvDate.textContent = today();
// Draft readiness pill
ready = !!specialty.value && reason.value.trim().length > 0;
draftPill.classList.toggle("is-ready", ready);
draftPill.textContent = ready ? "Ready to send" : "Draft";
}
form.addEventListener("input", updatePreview);
form.addEventListener("change", updatePreview);
/* ── Validation ── */
function clearError(fieldEl, errId) {
fieldEl.classList.remove("invalid");
const err = document.getElementById(errId);
if (err) err.hidden = true;
}
function showError(fieldEl, errId) {
fieldEl.classList.add("invalid");
const err = document.getElementById(errId);
if (err) err.hidden = false;
}
specialty.addEventListener("change", () =>
clearError(specialty.closest(".field"), "err-specialty")
);
reason.addEventListener("input", () => {
if (reason.value.trim()) clearError(reason.closest(".field"), "err-reason");
});
/* ── Reference number generator ── */
function makeRef() {
const year = new Date().getFullYear();
const n = Math.floor(100 + Math.random() * 9900);
return "REF-" + year + "-" + String(n).padStart(4, "0");
}
/* ── Submit ── */
form.addEventListener("submit", (e) => {
e.preventDefault();
let firstInvalid = null;
if (!specialty.value) {
showError(specialty.closest(".field"), "err-specialty");
firstInvalid = firstInvalid || specialty;
}
if (!reason.value.trim()) {
showError(reason.closest(".field"), "err-reason");
firstInvalid = firstInvalid || reason;
}
if (firstInvalid) {
firstInvalid.focus();
toast("Please complete the required fields.");
return;
}
const ref = makeRef();
const attach = selectedAttachments();
confirmRef.textContent = ref;
cfSpec.textContent = specialty.value;
cfUrg.textContent = currentUrgency;
cfAttach.textContent = attach.length ? attach.join(", ") : "None";
pvRef.textContent = ref;
previewCard.hidden = true;
confirmCard.hidden = false;
// Lock the form during confirmation
form
.querySelectorAll("input, select, textarea, button")
.forEach((el) => (el.disabled = true));
toast("Referral " + ref + " sent successfully.");
});
/* ── New referral / reset ── */
function resetAll() {
form.reset();
form
.querySelectorAll("input, select, textarea, button")
.forEach((el) => (el.disabled = false));
confirmCard.hidden = true;
previewCard.hidden = false;
pvRef.textContent = "REF-pending";
// Reset urgency to default
setUrgency(segBtns[0]);
clearError(specialty.closest(".field"), "err-specialty");
clearError(reason.closest(".field"), "err-reason");
document.getElementById("count-reason").textContent = "0 / 400";
document.getElementById("count-history").textContent = "0 / 600";
document
.getElementById("count-reason")
.classList.remove("near");
document
.getElementById("count-history")
.classList.remove("near");
updatePreview();
}
newBtn.addEventListener("click", () => {
resetAll();
specialty.focus();
});
form.addEventListener("reset", () => {
// Defer so native reset clears values first
setTimeout(() => {
setUrgency(segBtns[0]);
updatePreview();
toast("Form cleared.");
}, 0);
});
// Initial render
updatePreview();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap"
/>
<link rel="stylesheet" href="style.css" />
<title>Referral Request · Northpoint Clinic</title>
</head>
<body>
<main class="ref">
<!-- ── Form column ── -->
<form class="ref-form" id="referral-form" novalidate>
<header class="ref-head">
<div>
<p class="eyebrow">Northpoint Clinic</p>
<h1>Referral / consult request</h1>
<p class="sub">
Send a specialist consult request. The summary on the right
updates as you complete the form.
</p>
</div>
<span class="draft-pill" id="draft-pill">Draft</span>
</header>
<!-- Patient -->
<section class="field">
<label class="lbl">Patient</label>
<div class="patient-chip" aria-label="Selected patient, read only">
<span class="avatar" aria-hidden="true">AM</span>
<div class="patient-meta">
<strong>Amara Mensah</strong>
<span>DOB 14 Mar 1987 · MRN NPC-44120 · F</span>
</div>
<span class="chip-tag" aria-hidden="true">
<svg viewBox="0 0 24 24" width="13" height="13" aria-hidden="true">
<path
fill="currentColor"
d="M6 10V8a6 6 0 1 1 12 0v2h1a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-9a1 1 0 0 1 1-1zm2 0h8V8a4 4 0 1 0-8 0z"
/>
</svg>
Locked
</span>
</div>
</section>
<!-- Refer to specialty -->
<section class="field">
<label class="lbl" for="specialty">
Refer to specialty <span class="req" aria-hidden="true">*</span>
</label>
<div class="select-wrap">
<select id="specialty" name="specialty" required>
<option value="" selected disabled hidden>
Select a specialty…
</option>
<option value="Cardiology">Cardiology</option>
<option value="Dermatology">Dermatology</option>
<option value="Endocrinology">Endocrinology</option>
<option value="Gastroenterology">Gastroenterology</option>
<option value="Neurology">Neurology</option>
<option value="Orthopedics">Orthopedics</option>
<option value="Pulmonology">Pulmonology</option>
<option value="Rheumatology">Rheumatology</option>
</select>
<svg class="caret" viewBox="0 0 24 24" aria-hidden="true">
<path
fill="currentColor"
d="M7 10l5 5 5-5z"
/>
</svg>
</div>
<p class="err" id="err-specialty" hidden>
Please choose a specialty to refer to.
</p>
</section>
<!-- Urgency -->
<section class="field">
<span class="lbl" id="urgency-label">Urgency</span>
<div
class="seg"
role="radiogroup"
aria-labelledby="urgency-label"
id="urgency"
>
<button
type="button"
class="seg-btn is-active"
role="radio"
aria-checked="true"
data-value="Routine"
data-tone="ok"
>
Routine
</button>
<button
type="button"
class="seg-btn"
role="radio"
aria-checked="false"
data-value="Urgent"
data-tone="warn"
>
Urgent
</button>
<button
type="button"
class="seg-btn"
role="radio"
aria-checked="false"
data-value="Emergent"
data-tone="danger"
>
Emergent
</button>
</div>
<p class="hint" id="urgency-hint">
Routine consults are typically scheduled within 2–4 weeks.
</p>
</section>
<!-- Reason -->
<section class="field">
<div class="lbl-row">
<label class="lbl" for="reason">
Reason for referral <span class="req" aria-hidden="true">*</span>
</label>
<span class="counter" id="count-reason">0 / 400</span>
</div>
<textarea
id="reason"
name="reason"
rows="3"
maxlength="400"
placeholder="e.g. Persistent exertional chest tightness over 6 weeks; requesting cardiology evaluation."
required
></textarea>
<p class="err" id="err-reason" hidden>
Please describe the reason for this referral.
</p>
</section>
<!-- Relevant history -->
<section class="field">
<div class="lbl-row">
<label class="lbl" for="history">Relevant history</label>
<span class="counter" id="count-history">0 / 600</span>
</div>
<textarea
id="history"
name="history"
rows="3"
maxlength="600"
placeholder="Pertinent conditions, medications, allergies, prior workup or imaging."
></textarea>
</section>
<!-- Attach results -->
<section class="field">
<span class="lbl">Attach results</span>
<div class="check-list" id="attachments">
<label class="check">
<input type="checkbox" name="attach" value="Labs" />
<span class="box" aria-hidden="true"></span>
<span class="check-body">
<strong>Labs</strong>
<small>CBC, CMP, lipid panel — last 90 days</small>
</span>
</label>
<label class="check">
<input type="checkbox" name="attach" value="Imaging" />
<span class="box" aria-hidden="true"></span>
<span class="check-body">
<strong>Imaging</strong>
<small>Reports and study links on file</small>
</span>
</label>
<label class="check">
<input type="checkbox" name="attach" value="Notes" />
<span class="box" aria-hidden="true"></span>
<span class="check-body">
<strong>Clinical notes</strong>
<small>Recent progress and visit notes</small>
</span>
</label>
</div>
</section>
<footer class="ref-actions">
<button type="reset" class="btn ghost" id="clear-btn">
Clear form
</button>
<button type="submit" class="btn solid" id="submit-btn">
Send referral
</button>
</footer>
</form>
<!-- ── Live preview column ── -->
<aside class="preview" aria-label="Referral preview">
<div class="preview-card" id="preview-card">
<div class="preview-head">
<div class="brand">
<span class="brand-dot" aria-hidden="true"></span>
<span>Northpoint Clinic</span>
</div>
<span class="badge urg ok" id="pv-urgency">Routine</span>
</div>
<p class="pv-kicker">Referral / consult request</p>
<div class="pv-patient">
<span class="avatar sm" aria-hidden="true">AM</span>
<div>
<strong>Amara Mensah</strong>
<span>MRN NPC-44120 · DOB 14 Mar 1987</span>
</div>
</div>
<dl class="pv-grid">
<div class="pv-row">
<dt>Refer to</dt>
<dd id="pv-specialty" class="empty">Not selected</dd>
</div>
<div class="pv-row">
<dt>Referring provider</dt>
<dd>Dr. Lena Okafor · Internal Medicine</dd>
</div>
</dl>
<div class="pv-block">
<dt>Reason for referral</dt>
<dd id="pv-reason" class="empty">—</dd>
</div>
<div class="pv-block">
<dt>Relevant history</dt>
<dd id="pv-history" class="empty">—</dd>
</div>
<div class="pv-block">
<dt>Attachments</dt>
<dd id="pv-attach" class="empty">None selected</dd>
</div>
<div class="pv-foot">
<span id="pv-date">—</span>
<span class="pv-ref" id="pv-ref">REF-pending</span>
</div>
</div>
<!-- Confirmation overlay (hidden until submit) -->
<div class="confirm-card" id="confirm-card" hidden>
<div class="confirm-mark" aria-hidden="true">
<svg viewBox="0 0 24 24" width="30" height="30">
<path
fill="none"
stroke="currentColor"
stroke-width="2.4"
stroke-linecap="round"
stroke-linejoin="round"
d="M5 13l4 4L19 7"
/>
</svg>
</div>
<h2>Referral sent</h2>
<p class="confirm-sub">
Your consult request has been routed to the receiving specialty.
You will be notified when it is accepted.
</p>
<div class="ref-number">
<span class="ref-label">Reference number</span>
<strong id="confirm-ref">REF-2026-0481</strong>
</div>
<ul class="confirm-list" id="confirm-list">
<li><span class="k">Specialty</span><span id="cf-spec">—</span></li>
<li><span class="k">Urgency</span><span id="cf-urg">—</span></li>
<li>
<span class="k">Attachments</span><span id="cf-attach">—</span>
</li>
</ul>
<button type="button" class="btn ghost full" id="new-btn">
New referral
</button>
</div>
</aside>
</main>
<div class="toast" id="toast" hidden role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Referral / Consult Request
A two-column referral workspace for routing a patient to a specialist. The patient is fixed as a locked, read-only chip so the request can never be misattributed, while the Refer to specialty select, a segmented Routine / Urgent / Emergent urgency control, and two textareas for the reason and relevant history make up the body of the request. An Attach results checklist lets the referrer pull in Labs, Imaging and clinical Notes already on file.
Alongside the form sits a live preview card that mirrors the referral as it is written — the urgency badge changes tone, the specialty, reason, history and attachment tags fill in, and a draft pill flips from Draft to Ready to send once the required fields are present. Character counters track each textarea and the urgency control is fully keyboard-operable with arrow keys.
Submitting validates the required specialty and reason, surfacing inline errors and a toast if anything is missing. When the request is complete the preview flips to a confirmation state with a generated reference number such as REF-2026-0481, a summary of the routed details, and a path to start a new referral. All interactions are vanilla JS.
Illustrative UI only — not intended for real medical use.