More Cards Snippets
Quiz Card — Free HTML CSS JS Snippet
Quiz Card · Cards · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Quiz Card — Multiple Choice, Instant Feedback, Scoring & Results Ring

An interactive quiz is a powerful engagement tool used in e-learning, lead generation, onboarding, and content marketing — the single-question relative is the poll widget. This snippet provides a complete multiple-choice quiz with a progress bar, lettered answer options, instant correct/incorrect feedback, live scoring, and an animated circular results ring at the end showing the percentage score with a performance message.
The data-driven question model
Questions live in a QUESTIONS array, each with a prompt, an options array, and the index of the correct answer. renderQuestion() builds the options as buttons with letter keys (A, B, C, D) from this data. This separation of content from logic means adding, editing, or shuffling questions never touches the rendering or scoring code — you just edit the array.
Instant answer feedback
When an option is clicked, select() locks all options (preventing changes), highlights the correct answer in green with a checkmark, and if the choice was wrong, marks it red with a cross. This immediate reveal is a proven learning reinforcement pattern — showing the right answer at the moment of choice helps retention far better than deferring feedback to the end. The answered flag prevents re-answering after a choice is locked.
Scoring and progression
A correct answer increments the score and updates the points chip in the header. The Next button stays disabled until an answer is chosen, enforcing that the user engages with each question. On the final question, the button relabels to Finish and triggers the results screen instead of advancing.
The animated results ring
The results screen shows a circular SVG progress ring identical in technique to a radial gauge: stroke-dasharray equals the circumference and stroke-dashoffset animates to reveal the proportion matching the score percentage. A short setTimeout before setting the offset ensures the browser registers the initial state, so the ring animates from empty rather than snapping. The percentage counts in the centre and a performance message adapts to the score band.
The progress bar
A linear progress bar at the top fills proportionally to the current question number, giving users a clear sense of how far through the quiz they are — important for completion rates, since an unknown length discourages finishing.
Restart
The Try Again button resets the score, current index, ring offset, and visibility, returning to the first question — enabling repeat attempts without a page reload, which is valuable for practice quizzes and study tools.
Build with AI
Build, Understand, Optimize, and Extend It With AI
You do not have to trace the quiz's state machine by hand to see how it holds together. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why the answered flag guards select() against re-answering, and why the results ring's stroke-dashoffset animation is set inside a setTimeout rather than immediately after the results panel is shown. The same assistant can help optimize it — ask whether re-querying all .option elements from the DOM on every select() call scales poorly for quizzes with many questions rendered ahead of time, or how to avoid re-running querySelectorAll when the options array is already available in memory. It is just as useful for extending the quiz: ask it to add a per-question countdown timer, shuffle both question order and option order while keeping the correct index accurate, or persist scores across sessions with localStorage. Treat the code less like a finished artifact and more like a starting point for a conversation.
Prompt to recreate it
Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:
Build a multiple-choice quiz card in plain HTML, CSS, and JavaScript with no framework or library.
Requirements:
- Drive the entire quiz from a single QUESTIONS array of objects, each with a question string, an array of option strings, and the zero-based index of the correct option — no question content or scoring logic hardcoded outside this array.
- Render lettered option buttons (A, B, C, D) dynamically from the current question's options array, and show a progress bar whose width is (current question index + 1) divided by total questions.
- On selecting an option: lock all options from further clicks using an answered boolean flag, immediately reveal the correct answer in a distinct color with a checkmark, and if the clicked option was wrong, mark that option in a different color with an X — do not wait for a "submit" step, feedback must be instant on click.
- Keep the Next button disabled until an option has been selected for the current question, and relabel it to "Finish" on the last question.
- On finishing, hide the quiz and show a results panel with an SVG ring whose stroke-dasharray equals its circumference and whose stroke-dashoffset animates from fully-offset (empty) to a value representing the percentage score — the offset change must be deferred by one tick (e.g. a short setTimeout) after the panel becomes visible so the transition actually plays instead of snapping instantly to its end state.
- Show an adaptive message based on the score band (e.g. a distinct message for a perfect score versus a passing score versus a low score), and include a restart function that resets score, current question index, and the ring's offset without a full page reload.Step by step
How to Use
- 1Answer each questionClick one of the lettered options. The correct answer turns green and your choice turns red if it was wrong — feedback is instant and the answer locks.
- 2Advance through the quizAfter answering, click Next to move on. The progress bar fills and the points chip tracks your running score. The button reads Finish on the last question.
- 3View your resultsAt the end, an animated ring shows your percentage, the exact score, and a message that adapts to how well you did.
- 4Retake the quizClick Try Again to reset and start over from the first question — no page reload needed.
- 5Add your own questionsEdit the QUESTIONS array in the JS. Each entry needs a q (prompt), an opts array, and correct (the zero-based index of the right answer). Add as many as you like — everything scales automatically.
- 6Export for your frameworkClick "JSX" for a React component using useState for the current index and score. Click "Vue" for a Vue 3 SFC with reactive quiz state.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
Edit the QUESTIONS array at the top of the JavaScript. Each question is an object with three keys: q (the question text), opts (an array of answer strings, typically four), and correct (the zero-based index of the right answer within opts). Add as many question objects as you want — the counter, progress bar, scoring, and results all scale to the array length automatically with no other changes.
The ring uses stroke-dasharray set to its circumference and stroke-dashoffset to control how much is revealed. It starts fully offset (empty). When the results screen shows, a short setTimeout delays setting the final offset by one tick. This delay lets the browser paint the empty state first, so the transition animates smoothly from 0 to the score percentage instead of snapping instantly to the final value — the same technique used for any reveal-on-mount CSS transition.
Shuffle the QUESTIONS array with a Fisher-Yates shuffle before the quiz starts. To shuffle answer options too, map each question's opts into objects that remember whether each was the correct one, shuffle that array, then derive the new correct index from where the originally-correct option landed. Track the correct answer by identity rather than a fixed index so shuffling never breaks scoring.
Store current (question index), score, and answered in useState. Render the current question from QUESTIONS[current]. On option click, if not answered, set answered true, update the option styles via derived state (compare each index to the correct answer), and increment score for a correct pick. Next increments current or, on the last question, flips a showResults flag. Compute the ring dashoffset from score / total for the results view.