Skip to main content
:root { --hfma-navy: #003480; --text: #1b1b1b; --muted: #5a5a5a; --line: #d8d8d8; --soft: #f6f7f9; --focus: #6b8fd6; --white: #ffffff; --max-width: 900px; } * { box-sizing: border-box; } html { font-family: Arial, Helvetica, sans-serif; color: var(--text); background: var(--white); } body { margin: 0; min-height: 100vh; line-height: 1.45; } button, input { font: inherit; } button { cursor: pointer; } button:focus-visible, input:focus-visible, a:focus-visible { outline: 3px solid var(--focus); outline-offset: 3px; } .site-header { border-bottom: 1px solid #111; background: #fff; } .site-header .shell { min-height: 72px; display: flex; align-items: center; gap: 16px; } .shell { width: min(calc(100% - 40px), var(--max-width)); margin: 0 auto; } .brand { color: var(--hfma-navy); font-size: 1.35rem; font-weight: 800; letter-spacing: 0.02em; } .study-name { padding-left: 16px; border-left: 1px solid var(--line); color: var(--muted); font-size: 0.95rem; } .main-content { padding-top: 56px; padding-bottom: 80px; } .screen { max-width: 760px; } .hidden { display: none !important; } .eyebrow { margin: 0 0 10px; color: var(--hfma-navy); font-size: 0.82rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; } h1 { margin: 0 0 18px; max-width: 720px; font-size: clamp(1.9rem, 4vw, 2.7rem); line-height: 1.12; font-weight: 700; } h2 { margin-top: 0; font-size: 1.15rem; } .lede { max-width: 720px; margin: 0 0 28px; font-size: 1.1rem; color: #333; } .info-card { margin: 0 0 28px; padding: 22px 24px; background: var(--soft); border-left: 4px solid var(--hfma-navy); } .info-card ul { margin: 0; padding-left: 22px; } .info-card li + li { margin-top: 8px; } .field-label { display: block; margin-bottom: 8px; font-weight: 700; } .optional, .field-help { color: var(--muted); font-weight: 400; } .text-input { width: min(100%, 420px); padding: 12px 14px; border: 1px solid #777; border-radius: 2px; background: #fff; } .field-help { margin: 7px 0 24px; font-size: 0.9rem; } .submission-note { min-height: 1.4em; margin: 0 0 18px; color: var(--muted); font-size: 0.92rem; } .button:disabled { cursor: wait; opacity: 0.65; } .button { min-height: 44px; border-radius: 2px; padding: 10px 18px; border: 1px solid transparent; font-weight: 700; } .button-primary { background: var(--hfma-navy); color: #fff; } .button-primary:hover { background: #00275f; } .button-secondary { background: #fff; color: var(--hfma-navy); border-color: var(--hfma-navy); } .button-secondary:hover { background: #f3f6fb; } .button-link { padding-left: 6px; padding-right: 6px; background: transparent; color: #444; text-decoration: underline; } .task-meta { display: flex; justify-content: space-between; gap: 20px; margin-bottom: 28px; color: var(--muted); font-size: 0.92rem; } .task-question { font-size: clamp(1.55rem, 3.2vw, 2.15rem); } .instruction { margin: -5px 0 26px; color: var(--muted); } .breadcrumb { display: flex; flex-wrap: wrap; gap: 7px; min-height: 24px; margin-bottom: 18px; color: var(--muted); font-size: 0.92rem; } .breadcrumb-item:not(:last-child)::after { content: "›"; margin-left: 7px; color: #888; } .tree-panel { border-top: 1px solid #111; border-bottom: 1px solid #111; padding: 8px 0; } /* The top-level navigation is shown horizontally to better match a website main nav. Once a participant enters a section, the child options return to a vertical list. */ .tree-panel.root-nav { display: flex; align-items: stretch; gap: 0; padding: 0; overflow-x: auto; } .tree-panel.root-nav .tree-option { flex: 1 1 0; width: auto; min-width: 130px; min-height: 64px; justify-content: center; padding: 12px 16px; border-right: 1px solid #e1e1e1; border-bottom: 0; text-align: center; } .tree-panel.root-nav .tree-option:last-child { border-right: 0; } .tree-panel.root-nav .tree-option .arrow { margin-left: 2px; } .tree-panel.branch-nav { display: block; } .nav-heading { margin: 22px 0 7px; color: #555; font-size: 0.82rem; font-weight: 700; text-transform: none; } .nav-heading:first-child { margin-top: 8px; } .tree-option { width: 100%; display: flex; justify-content: space-between; align-items: center; gap: 18px; min-height: 49px; padding: 11px 12px; border: 0; border-bottom: 1px solid #e7e7e7; background: #fff; color: #111; text-align: left; font-weight: 600; } .tree-option:last-child { border-bottom: 0; } .tree-option:hover { background: var(--soft); } .tree-option .arrow { flex: 0 0 auto; font-size: 1.15rem; color: #777; } .task-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin-top: 22px; } .noscript { margin: 20px; padding: 16px; border: 2px solid #a40000; } @media (max-width: 620px) { .shell { width: min(calc(100% - 28px), var(--max-width)); } .main-content { padding-top: 36px; } .site-header .shell { min-height: 62px; } .tree-panel.root-nav { flex-wrap: wrap; overflow-x: visible; } .tree-panel.root-nav .tree-option { flex: 1 1 45%; min-width: 0; border-bottom: 1px solid #e7e7e7; } .task-actions { align-items: stretch; } .task-actions .button { width: 100%; } } /* HFMA TREE TEST -------------- The two sections you are most likely to edit are: 1) NAVIGATION 2) TASKS The interface, timing, path tracking, scoring, CSV debug export, and Netlify form submission are handled below those sections. */ const SETTINGS = { studyName: "HFMA Navigation Study", randomizeTasks: true, showTimerToParticipant: false }; // A/B test for the main navigation label. Participants are assigned 50/50. // For QA, add ?variant=A or ?variant=B to the URL to force a version. const NEWS_NAV_VARIANTS = { A: "News & insight", B: "News, insights & resources" }; const CANONICAL_NEWS_LABEL = "News, insights & resources"; /* ============================================================== 1) NAVIGATION ============================================================== */ const NAVIGATION = [ { label: "Membership", type: "leaf" }, { label: "Learn", type: "branch", sections: [ { heading: "Popular", items: [ "CRCR", "CHCRS", "Maintain your certification" ] }, { heading: "By type", items: [ "Certifications", "Webinars", "Micro-credentialing", "Organizational learning", "Education catalog" ] }, { heading: "", items: [ "HFMA Learning Center" ] } ] }, { label: "Events", type: "branch", sections: [ { heading: "", items: [ "Revenue Cycle Conference 27", "Leadership Summit 27", "Annual Conference 27", "Local chapter events", "Past events", "Live webinars", "View all events" ] } ] }, { label: "News, insights & resources", type: "branch", sections: [ { heading: "Featured", items: [ "Healthcare cost containment strategies shift" ] }, { heading: "News and resources", items: [ "Latest news", "Regulatory & accounting resources", "hfm magazine", "Special reports", "Podcasts" ] }, { heading: "Data & insights", items: [ "Data & insights overview", "MAP Initiative", "Peer Review", "Salary & compensation data", "Outlook trends surveys" ] }, { heading: "Popular topics", items: [ "Revenue Cycle Management", "Payment and Reimbursement", "Finance and Business Strategy", "Technology", "Leadership", "Compliance", "Affordability", "Alternative Payment Models and Value-Based Care" ] }, { heading: "", items: [ "View all topics" ] } ] }, { label: "Career resources", type: "branch", sections: [ { heading: "", items: [ "Job Bank", "Career self-assessment", "Career pathways", "Emerging leaders mentorship program", "Compensation benchmarks", "Certifications" ] } ] } ]; /* ============================================================== 2) TASKS Each answer is a valid final path. You can allow multiple correct destinations by adding another array inside answers. Example: answers: [ ["Learn", "Webinars"], ["Events", "Live Webinars"] ] ============================================================== */ const TASKS = [ { id: "maintain-certification", prompt: "You already have an HFMA certification and want to find information about keeping it current. Where would you go?", answers: [["Learn", "Maintain your certification"]] }, { id: "rcc27", prompt: "You want information about the 2027 Revenue Cycle Conference. Where would you go?", answers: [["Events", "RCC27"]] }, { id: "chapter-events", prompt: "You want to find an upcoming event hosted by an HFMA local chapter. Where would you go?", answers: [["Events", "Local chapter events"]] }, { id: "regulatory-resources", prompt: "You need HFMA resources about regulatory and accounting issues. Where would you go?", answers: [["News, insights & resources", "Regulatory & accounting resources"]] }, { id: "latest-news", prompt: "You want to see the latest healthcare finance news from HFMA. Where would you go?", answers: [["News, insights & resources", "Latest news"]] }, { id: "value-based-care", prompt: "You want HFMA content about value-based care and alternative payment models. Where would you go?", answers: [["News, insights & resources", "Alternative Payment Models and Value-Based Care"]] }, { id: "revenue-cycle-kpis", prompt: "Find KPIs for revenue cycle excellence.", answers: [["News, insights & resources", "MAP Initiative"]] }, { id: "job-bank", prompt: "You are looking for healthcare finance job openings. Where would you go?", answers: [["Career resources", "Job Bank"]] }, { id: "career-assessment", prompt: "You want to assess your current career skills and identify possible next steps. Where would you go?", answers: [["Career resources", "Career self-assessment"]] } ]; /* ============================================================== APPLICATION CODE ============================================================== */ const $ = (selector) => document.querySelector(selector); const screens = { intro: $("#intro-screen"), task: $("#task-screen"), between: $("#between-screen"), complete: $("#complete-screen") }; const elements = { participantId: $("#participant-id"), startButton: $("#start-button"), taskProgress: $("#task-progress"), taskTimer: $("#task-timer"), taskTitle: $("#task-title"), breadcrumb: $("#breadcrumb"), treePanel: $("#tree-panel"), backButton: $("#back-button"), chooseCurrentButton: $("#choose-current-button"), giveUpButton: $("#give-up-button"), nextButton: $("#next-button"), submitStatus: $("#submit-status"), retryButton: $("#retry-button"), downloadButton: $("#download-button") }; const state = { participantId: "", sessionId: createSessionId(), startedAt: null, completedAt: null, tasks: [], taskIndex: 0, currentTopLevel: null, taskStartedAt: null, timerInterval: null, currentEvents: [], results: [], lastSubmissionPayload: null, researchSubmitted: false, variant: "", navigation: [] }; const DEBUG = new URLSearchParams(window.location.search).get("debug") === "1"; function createSessionId() { if (window.crypto && crypto.randomUUID) return crypto.randomUUID(); return `session-${Date.now()}-${Math.random().toString(16).slice(2)}`; } function assignVariant() { const requested = new URLSearchParams(window.location.search).get("variant")?.toUpperCase(); if (requested === "A" || requested === "B") return requested; return Math.random() < 0.5 ? "A" : "B"; } function buildNavigationForVariant(variant) { return NAVIGATION.map((item) => { if (item.label !== CANONICAL_NEWS_LABEL) return item; return { ...item, label: NEWS_NAV_VARIANTS[variant] }; }); } function normalizePath(path) { if (!path.length) return []; const normalized = [...path]; if (normalized[0] === NEWS_NAV_VARIANTS.A || normalized[0] === NEWS_NAV_VARIANTS.B) { normalized[0] = CANONICAL_NEWS_LABEL; } return normalized; } function shuffle(array) { const copy = [...array]; for (let i = copy.length - 1; i > 0; i -= 1) { const j = Math.floor(Math.random() * (i + 1)); [copy[i], copy[j]] = [copy[j], copy[i]]; } return copy; } function showScreen(name) { Object.values(screens).forEach((screen) => screen.classList.add("hidden")); screens[name].classList.remove("hidden"); window.scrollTo({ top: 0, behavior: "instant" }); } function startStudy() { state.participantId = elements.participantId.value.trim(); state.startedAt = new Date().toISOString(); state.variant = assignVariant(); state.navigation = buildNavigationForVariant(state.variant); state.tasks = SETTINGS.randomizeTasks ? shuffle(TASKS) : [...TASKS]; state.taskIndex = 0; state.results = []; showTask(); } function showTask() { const task = state.tasks[state.taskIndex]; state.currentTopLevel = null; state.currentEvents = []; state.taskStartedAt = performance.now(); elements.taskProgress.textContent = `Task ${state.taskIndex + 1} of ${state.tasks.length}`; elements.taskTitle.textContent = task.prompt; elements.backButton.classList.add("hidden"); elements.chooseCurrentButton.classList.add("hidden"); renderRoot(); startTimer(); showScreen("task"); elements.taskTitle.focus?.(); } function startTimer() { clearInterval(state.timerInterval); elements.taskTimer.classList.toggle("hidden", !SETTINGS.showTimerToParticipant); updateTimer(); state.timerInterval = setInterval(updateTimer, 1000); } function updateTimer() { if (!state.taskStartedAt) return; const seconds = Math.floor((performance.now() - state.taskStartedAt) / 1000); const mins = Math.floor(seconds / 60); const secs = String(seconds % 60).padStart(2, "0"); elements.taskTimer.textContent = `${mins}:${secs}`; } function renderRoot() { state.currentTopLevel = null; elements.breadcrumb.innerHTML = 'Main navigation'; elements.backButton.classList.add("hidden"); elements.chooseCurrentButton.classList.add("hidden"); elements.treePanel.classList.add("root-nav"); elements.treePanel.classList.remove("branch-nav"); elements.treePanel.innerHTML = ""; state.navigation.forEach((item) => { const button = createOptionButton(item.label, item.type === "branch"); button.addEventListener("click", () => { recordEvent("click", [item.label]); if (item.type === "leaf") { finishTask([item.label], "selected"); } else { state.currentTopLevel = item; renderBranch(item); } }); elements.treePanel.appendChild(button); }); } function renderBranch(branch) { elements.treePanel.classList.remove("root-nav"); elements.treePanel.classList.add("branch-nav"); elements.breadcrumb.innerHTML = ["Main navigation", branch.label] .map((label) => `${escapeHtml(label)}`) .join(""); elements.backButton.classList.remove("hidden"); elements.chooseCurrentButton.classList.remove("hidden"); elements.chooseCurrentButton.textContent = `Choose ${branch.label}`; elements.treePanel.innerHTML = ""; branch.sections.forEach((section) => { if (section.heading) { const heading = document.createElement("div"); heading.className = "nav-heading"; heading.textContent = section.heading; elements.treePanel.appendChild(heading); } section.items.forEach((label) => { const button = createOptionButton(label, false); button.addEventListener("click", () => { const path = [branch.label, label]; recordEvent("click", path); finishTask(path, "selected"); }); elements.treePanel.appendChild(button); }); }); } function createOptionButton(label, hasChildren) { const button = document.createElement("button"); button.type = "button"; button.className = "tree-option"; const text = document.createElement("span"); text.textContent = label; button.appendChild(text); if (hasChildren) { const arrow = document.createElement("span"); arrow.className = "arrow"; arrow.setAttribute("aria-hidden", "true"); arrow.textContent = "›"; button.appendChild(arrow); } return button; } function goBack() { if (!state.currentTopLevel) return; recordEvent("back", [state.currentTopLevel.label]); renderRoot(); } function chooseCurrentPage() { if (!state.currentTopLevel) return; const path = [state.currentTopLevel.label]; recordEvent("select-current", path); finishTask(path, "selected"); } function giveUp() { recordEvent("give-up", state.currentTopLevel ? [state.currentTopLevel.label] : []); finishTask([], "gave-up"); } function recordEvent(type, path) { const elapsedMs = Math.round(performance.now() - state.taskStartedAt); state.currentEvents.push({ type, path: [...path], elapsed_ms: elapsedMs }); } function finishTask(selectedPath, selectionType) { clearInterval(state.timerInterval); const task = state.tasks[state.taskIndex]; const elapsedMs = Math.round(performance.now() - state.taskStartedAt); const canonicalSelectedPath = normalizePath(selectedPath); const accepted = task.answers.some((answer) => pathsEqual(answer, canonicalSelectedPath)); const firstClickEvent = state.currentEvents.find((event) => event.type === "click"); const usedBack = state.currentEvents.some((event) => event.type === "back"); let outcome = "failure"; if (selectionType === "gave-up") { outcome = "gave-up"; } else if (accepted) { outcome = usedBack ? "success-indirect" : "success-direct"; } state.results.push({ task_id: task.id, task_prompt: task.prompt, accepted_paths: task.answers, selected_path: selectedPath, selected_path_canonical: canonicalSelectedPath, first_click: firstClickEvent?.path?.[0] || "", outcome, time_seconds: Number((elapsedMs / 1000).toFixed(1)), events: [...state.currentEvents] }); showScreen("between"); } function nextTask() { state.taskIndex += 1; if (state.taskIndex >= state.tasks.length) { completeStudy(); } else { showTask(); } } async function completeStudy() { state.completedAt = new Date().toISOString(); state.researchSubmitted = false; const payload = buildPayload(); state.lastSubmissionPayload = payload; showScreen("complete"); elements.submitStatus.textContent = "Submitting your responses…"; elements.retryButton.classList.add("hidden"); state.researchSubmitted = await submitResearchPayload(payload); updateFinalSubmissionStatus(); if (DEBUG) { elements.downloadButton.classList.remove("hidden"); } } function buildPayload() { const summary = { ...summarizeResults(state.results), test_variant: state.variant, tested_label: NEWS_NAV_VARIANTS[state.variant] }; return { formName: "tree-test-results", participantId: state.participantId, sessionId: state.sessionId, variant: state.variant, testedLabel: NEWS_NAV_VARIANTS[state.variant], startedAt: state.startedAt, completedAt: state.completedAt, summary, results: state.results }; } function summarizeResults(results) { const direct = results.filter((r) => r.outcome === "success-direct").length; const indirect = results.filter((r) => r.outcome === "success-indirect").length; const failures = results.filter((r) => r.outcome === "failure").length; const gaveUp = results.filter((r) => r.outcome === "gave-up").length; const avgSeconds = results.length ? results.reduce((sum, r) => sum + r.time_seconds, 0) / results.length : 0; return { task_count: results.length, success_direct: direct, success_indirect: indirect, failures, gave_up: gaveUp, success_rate: results.length ? Number((((direct + indirect) / results.length) * 100).toFixed(1)) : 0, average_time_seconds: Number(avgSeconds.toFixed(1)) }; } async function submitResearchPayload(payload) { const formData = new URLSearchParams(); formData.append("form-name", payload.formName); formData.append("participant_id", payload.participantId); formData.append("session_id", payload.sessionId); formData.append("variant", payload.variant); formData.append("tested_label", payload.testedLabel); formData.append("started_at", payload.startedAt); formData.append("completed_at", payload.completedAt); formData.append("summary_json", JSON.stringify(payload.summary)); formData.append("results_json", JSON.stringify(payload.results)); try { const response = await fetch("/", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: formData.toString() }); if (!response.ok) throw new Error(`HTTP ${response.status}`); return true; } catch (error) { console.error("Research submission failed", error); return false; } } function updateFinalSubmissionStatus() { if (state.researchSubmitted) { elements.submitStatus.textContent = "Your responses have been submitted. You may close this page."; elements.retryButton.classList.add("hidden"); } else { elements.submitStatus.textContent = "We couldn't submit your responses. Please try again."; elements.retryButton.classList.remove("hidden"); } } async function retrySubmission() { elements.retryButton.classList.add("hidden"); elements.submitStatus.textContent = "Retrying submission…"; if (state.lastSubmissionPayload) { state.researchSubmitted = await submitResearchPayload(state.lastSubmissionPayload); } updateFinalSubmissionStatus(); } function pathsEqual(a, b) { return a.length === b.length && a.every((value, index) => value === b[index]); } function escapeHtml(value) { return String(value) .replaceAll("&", "&") .replaceAll("<", "<") .replaceAll(">", ">") .replaceAll('"', """) .replaceAll("'", "'"); } function downloadDebugCsv() { if (!state.lastSubmissionPayload) return; const headers = [ "participant_id", "session_id", "variant", "tested_label", "task_id", "task_prompt", "first_click", "selected_path", "outcome", "time_seconds", "click_events" ]; const rows = state.results.map((result) => [ state.participantId, state.sessionId, state.variant, NEWS_NAV_VARIANTS[state.variant], result.task_id, result.task_prompt, result.first_click, result.selected_path.join(" > "), result.outcome, result.time_seconds, JSON.stringify(result.events) ]); const csv = [headers, ...rows] .map((row) => row.map(csvEscape).join(",")) .join("\n"); const blob = new Blob([csv], { type: "text/csv;charset=utf-8" }); const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = `hfma-tree-test-${state.sessionId}.csv`; document.body.appendChild(link); link.click(); link.remove(); URL.revokeObjectURL(url); } function csvEscape(value) { const text = String(value ?? ""); return `"${text.replaceAll('"', '""')}"`; } elements.startButton.addEventListener("click", startStudy); elements.backButton.addEventListener("click", goBack); elements.chooseCurrentButton.addEventListener("click", chooseCurrentPage); elements.giveUpButton.addEventListener("click", giveUp); elements.nextButton.addEventListener("click", nextTask); elements.retryButton.addEventListener("click", retrySubmission); elements.downloadButton.addEventListener("click", downloadDebugCsv); HFMA Navigation Study

Website navigation research

Help us test a proposed HFMA website navigation.

You will be given a series of short tasks. For each task, choose where you would look first in the navigation. There are no right or wrong answers from your perspective—we are testing the navigation, not you.

How it works

  • Read one task at a time.
  • Select the navigation option you would use.
  • You can go back if you change your mind.
  • If you would stop looking, choose I can’t find it.

Use the ID provided to you in the email from HFMA. This step is important to receive your $10 Amazon Giftcard.

{{ loadingHeading }}

{{ loadingSubHeading }}

We’re having trouble logging you in.

For assistance, contact our Member Services Team.

Your session has expired.

Please reload the page and try again.