Pricing Tier Recommender Quiz — Free 3-Question Plan Picker (HTML/CSS/JS)
Pricing Tier Recommender Quiz · Pricing · Plain HTML, CSS & JS · Live preview
Copy & paste
Source Code
<div class="ptr-card">
<div class="ptr-head">
<div class="ptr-progress"><div class="ptr-progress-bar" id="ptrProgressBar"></div></div>
<span class="ptr-step-label" id="ptrStepLabel">Question 1 of 3</span>
</div>
<div class="ptr-questions" id="ptrQuestions">
<div class="ptr-q" data-q="0">
<h3>How many people need access?</h3>
<div class="ptr-options">
<button type="button" class="ptr-opt" data-q="0" data-v="solo">Just me</button>
<button type="button" class="ptr-opt" data-q="0" data-v="small">2–9 people</button>
<button type="button" class="ptr-opt" data-q="0" data-v="large">10+ people</button>
</div>
</div>
<div class="ptr-q" data-q="1" hidden>
<h3>Do you need SSO or audit logs?</h3>
<div class="ptr-options">
<button type="button" class="ptr-opt" data-q="1" data-v="yes">Yes, required</button>
<button type="button" class="ptr-opt" data-q="1" data-v="no">No, not needed</button>
</div>
</div>
<div class="ptr-q" data-q="2" hidden>
<h3>How many projects per month?</h3>
<div class="ptr-options">
<button type="button" class="ptr-opt" data-q="2" data-v="low">Under 5</button>
<button type="button" class="ptr-opt" data-q="2" data-v="mid">5–30</button>
<button type="button" class="ptr-opt" data-q="2" data-v="high">30+</button>
</div>
</div>
</div>
<div class="ptr-result" id="ptrResult" hidden>
<span class="ptr-result-tag">Recommended for you</span>
<h3 id="ptrResultTier">Team</h3>
<p id="ptrResultPrice">$79/mo</p>
<p class="ptr-result-why" id="ptrResultWhy"></p>
<button type="button" class="ptr-restart" id="ptrRestart">Start over</button>
</div>
</div>*{box-sizing:border-box;margin:0;padding:0}
body{font-family:system-ui,-apple-system,sans-serif;background:#0a0d16;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:24px}
.ptr-card{background:#111524;border:1px solid #1f2438;border-radius:18px;padding:26px;width:100%;max-width:400px;box-shadow:0 20px 50px rgba(0,0,0,.45)}
.ptr-head{margin-bottom:22px}
.ptr-progress{height:5px;border-radius:99px;background:#1c2136;overflow:hidden;margin-bottom:8px}
.ptr-progress-bar{height:100%;width:33.33%;background:linear-gradient(90deg,#38bdf8,#818cf8);border-radius:99px;transition:width .3s ease}
.ptr-step-label{font-size:11px;color:#6b7292;font-weight:600}
.ptr-q h3{font-size:17px;font-weight:700;color:#f0f2fb;margin-bottom:16px;line-height:1.35}
.ptr-options{display:flex;flex-direction:column;gap:9px}
.ptr-opt{font-family:inherit;text-align:left;background:#171c2e;border:1.5px solid #262c47;border-radius:11px;padding:13px 15px;color:#d3d6ec;font-size:13.5px;font-weight:600;cursor:pointer;transition:border-color .15s,background .15s,transform .1s}
.ptr-opt:hover{border-color:#4c5378;background:#1c2238}
.ptr-opt:active{transform:scale(.98)}
.ptr-result{text-align:center;padding-top:4px}
.ptr-result-tag{display:inline-block;font-size:10.5px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:#38bdf8;background:rgba(56,189,248,.12);padding:5px 11px;border-radius:999px;margin-bottom:14px}
.ptr-result h3{font-size:26px;font-weight:800;color:#f0f2fb;margin-bottom:4px}
.ptr-result p{font-size:14px;color:#8890b8;margin-bottom:14px}
.ptr-result-why{font-size:12.5px;color:#9ba1c9;line-height:1.5;background:#171c2e;border:1px solid #262c47;border-radius:11px;padding:12px 14px;margin-bottom:18px !important}
.ptr-restart{font-family:inherit;width:100%;background:none;border:1.5px solid #262c47;border-radius:10px;padding:11px;color:#9ba1c9;font-size:12.5px;font-weight:700;cursor:pointer;transition:border-color .15s}
.ptr-restart:hover{border-color:#4c5378}var TOTAL_QUESTIONS = 3;
var answers = {};
var current = 0;
var progressBar = document.getElementById('ptrProgressBar');
var stepLabel = document.getElementById('ptrStepLabel');
var questionsWrap = document.getElementById('ptrQuestions');
var resultEl = document.getElementById('ptrResult');
var resultTierEl = document.getElementById('ptrResultTier');
var resultPriceEl = document.getElementById('ptrResultPrice');
var resultWhyEl = document.getElementById('ptrResultWhy');
var restartBtn = document.getElementById('ptrRestart');
// Real decision logic: maps the three answers to a specific tier + reason.
function recommend(a) {
if (a.people === 'large' || a.sso === 'yes') {
return {
tier: 'Enterprise',
price: 'Custom pricing',
why: a.sso === 'yes'
? 'SSO and audit logs are Enterprise-only features, regardless of team size.'
: 'Teams of 10 or more get volume pricing, SSO, and priority support on Enterprise.'
};
}
if (a.people === 'small' || a.projects === 'high') {
return {
tier: 'Team',
price: '$79/mo',
why: a.projects === 'high'
? '30+ projects a month needs Team\'s higher usage limits and collaboration tools.'
: 'A 2\u20139 person team fits Team\'s shared workspace and seat limits.'
};
}
if (a.projects === 'mid') {
return {
tier: 'Pro',
price: '$29/mo',
why: 'Solo use with 5\u201330 projects a month is exactly what Pro\'s limits are built for.'
};
}
return {
tier: 'Starter',
price: '$9/mo',
why: 'Solo use with under 5 projects a month fits comfortably inside Starter.'
};
}
function updateProgress() {
var pct = Math.min(current, TOTAL_QUESTIONS) / TOTAL_QUESTIONS * 100;
progressBar.style.width = pct + '%';
stepLabel.textContent = current < TOTAL_QUESTIONS
? 'Question ' + (current + 1) + ' of ' + TOTAL_QUESTIONS
: 'Done';
}
function showQuestion(index) {
questionsWrap.querySelectorAll('.ptr-q').forEach(function (q) {
q.hidden = Number(q.dataset.q) !== index;
});
}
function finish() {
var result = recommend(answers);
questionsWrap.hidden = true;
resultEl.hidden = false;
resultTierEl.textContent = result.tier;
resultPriceEl.textContent = result.price;
resultWhyEl.textContent = result.why;
}
questionsWrap.querySelectorAll('.ptr-opt').forEach(function (btn) {
btn.addEventListener('click', function () {
var q = Number(btn.dataset.q);
var v = btn.dataset.v;
if (q === 0) answers.people = v;
if (q === 1) answers.sso = v;
if (q === 2) answers.projects = v;
current = q + 1;
updateProgress();
if (current < TOTAL_QUESTIONS) {
showQuestion(current);
} else {
finish();
}
});
});
restartBtn.addEventListener('click', function () {
answers = {};
current = 0;
questionsWrap.hidden = false;
resultEl.hidden = true;
showQuestion(0);
updateProgress();
});
showQuestion(0);
updateProgress();Extend with AI
Take This Snippet Further With AI
The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.
What's included
Features
About this UI Snippet
Pricing Tier Recommender Quiz — Recommend a Plan From Real Answers, Not a Guess

A pricing page with four tiers asks visitors to self-diagnose which one fits — a task most people skip. This snippet replaces that guesswork with a three-question quiz that walks someone through team size, whether they need SSO or audit logs, and monthly project volume, then recommends one specific tier from a real decision function evaluated over their answers, not a random pick or a static default.
A pure decision function, not a lookup table
recommend(a) is a plain function that takes the collected { people, sso, projects } answers and returns a tier through ordered conditional checks: any SSO requirement or a 10+ person team forces Enterprise regardless of other answers; a 2–9 person team or 30+ projects a month lands on Team; mid-volume solo use lands on Pro; everything else falls through to Starter. Because it's a function over real input, the same three answers always produce the same tier — there's no randomness and no answer combination that falls through without a result.
One question at a time, with progress
Only one question is visible at once (.ptr-q[hidden] toggled by index), with a progress bar and a "Question X of 3" label that update as current advances. This keeps the quiz feeling short and sequential rather than presenting all three questions in a wall at once — the pattern that keeps completion rates high on real onboarding quizzes.
A reasoned result, not just a name
The result screen doesn't just name a tier — resultWhyEl states the specific reason, built inside recommend() itself, referencing whichever answer actually drove the decision ("SSO and audit logs are Enterprise-only" vs. "a 2–9 person team fits Team's shared workspace"). Explaining the *why* is what makes a recommendation feel earned rather than arbitrary, and it doubles as a preview of the tier's actual selling points.
Answers accumulate in one object
Every click writes into a single answers object keyed by question (people, sso, projects), so recommend() always evaluates the full, current answer set — restarting via the button simply resets that object and the step index, with no leftover state from a previous run.
Where it fits
Place it above a pricing card grid so the recommended tier can be highlighted, follow it with a plan selector to confirm the choice, or route an Enterprise result straight to an enterprise pricing contact flow.
Customizing it
Add more questions, change the tier thresholds, or have recommend() return a confidence score alongside the tier so borderline cases can show a runner-up option too.
Step by step
How to Use
- 1Paste HTML, CSS, and JSQuestion 1 of 3 renders with a progress bar at 0%.
- 2Answer each questionThe progress bar and step label advance after each click.
- 3See the recommendationrecommend() evaluates all three answers into one specific tier.
- 4Read the reasoningThe result explains which answer drove the recommendation.
- 5Try different pathsRestart and answer differently — the tier changes accordingly.
- 6Wire up real tiersEdit recommend() with your own thresholds and plan names.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
It's a pure function, recommend(a), that evaluates the three collected answers through ordered conditional checks — no randomness anywhere. Any SSO requirement or a 10+ person team always forces Enterprise; a 2\u20139 person team or 30+ monthly projects always lands on Team; mid-volume solo use always lands on Pro; everything else falls through to Starter. The same answers always produce the same result.
SSO is checked with priority — a.sso === 'yes' recommends Enterprise regardless of team size, because SSO and audit logs are gated to that tier in this quiz's logic. The why text explicitly states that reason rather than the team-size reason, so the explanation always matches the answer that actually mattered.
Yes. The conditional chain in recommend() is exhaustive: Enterprise catches large teams or SSO need, Team catches small teams or high project volume, Pro catches mid volume, and the final return is an unconditional Starter fallback. There's no answer combination that falls through without producing a tier.
A bare tier name feels arbitrary; stating the specific reason (built inside recommend() itself, referencing whichever answer triggered that branch) makes the recommendation feel earned and gives a preview of that tier's actual value, which is more persuasive than a plan name alone.
Move answers and current into component state, and call the same pure recommend(answers) function once the last question is answered — it needs no DOM access and returns a plain {tier, price, why} object you render directly. The step-progression and restart logic map to standard state updates.