I needed a way to help contractors figure out which business software to use. Instead of building a React app, I built an interactive 5-step quiz with vanilla HTML, CSS, and JavaScript.
Here's how it works and how you can build your own recommendation engine.
The Concept
A multi-step quiz that:
- Asks 5 questions (trade, team size, budget, priorities, experience)
- Scores 7 software options against the answers
- Shows top 3 recommendations with match scores
- Has email capture and social sharing built in
All in a single HTML file. No build step. No dependencies.
The Architecture
1. Question Cards with Show/Hide
Each question is a div with a data-q attribute. Only one is visible at a time:
<div class="question-card active" data-q="1">
<h2>What type of work do you do?</h2>
<div class="options">
<div class="option" data-value="hvac" onclick="selectOption(this)">
<span class="option-icon">snowflake emoji</span>
<div class="option-text">
<strong>HVAC</strong>
<span>Heating, cooling, ventilation</span>
</div>
</div>
</div>
</div>
CSS handles the visibility:
.question-card { display: none; }
.question-card.active { display: block; }
2. Selection Logic
Store answers in a simple object:
const answers = {};
function selectOption(el) {
const card = el.closest('.question-card');
const q = card.dataset.q;
card.querySelectorAll('.option').forEach(o =>
o.classList.remove('selected'));
el.classList.add('selected');
answers[q] = el.dataset.value;
document.getElementById('nextBtn' + q).disabled = false;
}
3. The Scoring Engine
This is the interesting part. Each software product has arrays of compatible values for each dimension:
const software = {
jobber: {
name: 'Jobber',
trades: ['hvac', 'plumbing', 'electrical'],
sizes: ['solo', 'small', 'medium'],
budgets: ['moderate', 'growth'],
priorities: ['scheduling', 'invoicing'],
experience: ['none', 'basic'],
score: 8.5,
pricing: 'From $49/month'
}
};
function scoreMatch(sw) {
let score = 0;
if (sw.trades.includes(answers['1'])) score += 3;
if (sw.sizes.includes(answers['2'])) score += 3;
if (sw.budgets.includes(answers['3'])) score += 3;
if (sw.priorities.includes(answers['4'])) score += 2;
if (sw.experience.includes(answers['5'])) score += 1;
return score; // max 12
}
The weighting is deliberate: trade, size, and budget are deal-breakers (3 points each). Priority matters (2 points). Experience level is a nice-to-have (1 point).
4. Dynamic Results
Sort by score, take top 3, render with template literals:
const scored = Object.entries(software)
.map(([key, sw]) => ({ key, ...sw, matchScore: scoreMatch(sw) }))
.sort((a, b) => b.matchScore - a.matchScore)
.slice(0, 3);
container.innerHTML = scored.map((rec, i) => `
<div class="recommendation ${i === 0 ? 'top-pick' : ''}">
<div class="rec-badge">
${i === 0 ? 'BEST MATCH' : 'Runner Up'}
</div>
<h3>${rec.name}</h3>
<p>Match: ${rec.matchScore}/12</p>
</div>
`).join('');
5. Smooth Progress Bar
.progress-fill {
background: linear-gradient(90deg, #3b82f6, #8b5cf6);
height: 100%;
border-radius: 50px;
transition: width 0.4s ease;
}
Update width on each step transition for that satisfying fill effect.
Why No Framework?
For a tool like this:
- 0 KB of JavaScript dependencies
- Instant load time (important for SEO and user experience)
- Works everywhere (no polyfills needed)
- Easy to embed (paste HTML into any site)
The tradeoff is managing state manually. But with 5 questions and 7 products, that complexity is trivial.
The Business Side
This quiz pattern works for any recommendation engine:
- Lead generation via email capture in results
- Affiliate revenue from recommendation links
- SEO with FAQ schema for rich snippets
- Viral sharing with social buttons
One tool, four growth channels.
Try It
Live demo: Contractor Software Quiz
The pattern is reusable. Swap out the software database for your niche (hosting providers, project management tools, CRMs) and you have a recommendation engine.
All the other free tools are at toolkit.buildoutreach.io/tools
What approach do you prefer for interactive content — framework or vanilla? Let me know in the comments.
Top comments (0)