Frontend
A config-driven checkpoint quiz in vanilla JS, no build step
Niamh Doherty Dev.to (EN Zone)
16 views
Most product-training pages are either a CMS quiz plugin or a React app that needs a bundler. I wanted neither. The constraint was: drop two files on a static host, pass a config object, get a playable road-trip quiz.
The result is a small engine called RoadQuest. The interesting part is not the truck animation. It is the split between state and content.
The only API
Mount a node, pass data, stop.
<link rel="stylesheet" href="roadquest.css">
<div id="quest"></div>
<script src="roadquest.js"></script>
<script>
RoadQuest.init('#quest', {
brand: { name: 'Acme Training', tagline: 'Checkpoint quiz' },
hero: {
title: 'Training Quest',
description: 'Answer each question to complete the journey.'
},
checkpoints: [
{
topic: 'Safety',
question: 'Which answer is correct?',
options: ['First answer', 'Second answer', 'Third answer'],
correctIndex: 1,
explanation: 'The second answer is correct.'
}
]
});
</script>
init accepts a selector or a DOM node. There is no router, no JSX, no npm install. If checkpoints is missing or empty, the engine throws before it paints anything.
Content is data, the engine is a state machine
Each checkpoint is a record: question, options, correctIndex, optional topic / badge / explanation. The engine never knows what the quiz is about. That is the point.
Internally the game only has a handful of fields:
this.level = 0;
this.score = 0;
this.streak = 0;
this.firstTry = 0;
this.missedThisLevel = false;
Screens are start → quiz → drive animation → next checkpoint → finish. Wrong answers do not advance level. They mark missedThisLevel, reset the streak, and let the player retry immediately. A checkpoint still counts as cleared; first-try accuracy is what drops.
Scoring is deliberately cheap to explain:
if (i === q.correctIndex) {
if (!this.missedThisLevel) this.firstTry++;
this.streak++;
this.score += 100 + Math.min((this.streak - 1) * 20, 80);
} else {
this.missedThisLevel = true;
this.streak = 0;
this.score = Math.max(0, this.score - 25);
}
On finish, onComplete receives { score, accuracy }. You can log it, post it, or ignore it. The engine does not own analytics.
Theme without a design system
Colours are CSS custom properties written onto the mount node:
root.style.setProperty('--rq-' + key, merged[key]);
A second brand is a different config, not a fork. The same roadquest.js + roadquest.css pair runs a fictional barista onboarding quiz and a hardware training quiz. Copy the config file; leave the engine alone.
Why this shape works on a product site
The first production config is a nine-checkpoint training game I wrote for TruckNav. The questions cover setup and vehicle-profile basics a driver should confirm while parked. The engine source is on GitHub.
That config is still just data: route labels, a stageLength of 100 km, theme colours, and a cta href. Swap the checkpoints and the same engine becomes an onboarding quiz for a different team.
Two rules stayed non-negotiable and are worth copying if you build anything in this genre:
Escape every string that came from config before it hits innerHTML.
The quiz is training, not the product. Road signs and legal restrictions still win.
What I would change next
The engine is one IIFE on window. That is the right size for a GitHub Pages demo. If I were embedding it in a larger app I would export the class, keep innerHTML for the shell, and render answers with createElement only. The config schema would not need to change.
If you want a quiz on a static site and you do not want a framework for it, keep the questions in a data file and keep the game in about 300 lines of JavaScript. The brand should be a config object, not a rewrite.
Read original: https://dev.to/navcheckniamh/a-config-driven-checkpoint-quiz-in-vanilla-js-no-build-step-2eop
← Previous
Phantom Capacity: Why Texas Couldn't Tell Real Demand From Noise
Next →
Why `anthropics/skills` Earned 277 Stars Today: Reusable Agent Workflows, Not More Prompts
Related
validation-br 2.0: Validação em TypeScript, zero dependências
Frontend
52
Dev.to (EN Zone)
The same one-line table-parsing bug turned up in five separate tools. I fixed it four times.
Frontend
40
DEV Community
How to Price Web Design Projects: A Practical Guide for Freelancers
Frontend
36
Dev.to (EN Zone)
Post Apache Ossie, what distinguishes semantic layers?
Frontend
41
Dev.to (EN Zone)
Comments0
No comments yet — be the first