Synthesis Quiz
A data-driven multi-question quiz primitive. The caller supplies an array of questions, a pair of done-screen messages (perfect, needsWork), and an optional passThreshold — the panel walks through every question, tracks first-attempt correctness, and finishes with a synthesis grade screen that reports the score and the matching message.
Question 1 / 3
Pruning
Which signal best tells you a neuron has stopped contributing to the loss?
Customize
Scoring
2
Behaviour
Installation
npx shadcn@latest add https://craftbits.dev/r/synthesis-quiz.jsonUsage
import { SynthesisQuiz } from "@craft-bits/core";
const QUESTIONS = [
{
scenario: "Pruning",
question: "Which signal best tells you a neuron has stopped contributing?",
options: [
"Its gradient stays at or near zero across many batches.",
"Its weight magnitude is large.",
"Its activation is non-negative on every input.",
"Its incoming weights all share the same sign.",
],
correctIdx: 0,
explanation: "A dead neuron is one whose gradient flow has collapsed.",
},
// ...more questions
];
<SynthesisQuiz
questions={QUESTIONS}
doneMessages={{
perfect: "Three for three. You can ship the lesson.",
needsWork: "Re-read the explanations — the gaps are in the norm intuition.",
}}
passThreshold={2}
onComplete={(score) => persistResult(score)}
/>Disable retry on wrong, so the first pick locks the row regardless of correctness:
<SynthesisQuiz
questions={QUESTIONS}
doneMessages={{ perfect: "Clean.", needsWork: "Try again." }}
retryOnWrong={false}
/>Surface a hint after every retry — pass a string or a function of the wrong-attempt count:
<SynthesisQuiz
questions={QUESTIONS}
doneMessages={{ perfect: "Clean.", needsWork: "Try again." }}
wrongHint={(n) => (n === 1 ? "Look at the gradient signal." : "Hint two.")}
/>Understanding the component
- One row, locked once committed. Each question renders as a question prompt + scenario badge + option list. Picking the correct option commits the row, reveals the explanation, and surfaces a
Next questionbutton. Wrong picks bounce back to an unselected state under the defaultretryOnWrongpolicy. - First-attempt scoring. A question only counts toward the synthesis score if the student picks the correct option on their first attempt. Retried-into-correct picks still advance the quiz, but the final score reflects how much the student knew up front.
- Synthesis grade screen. Once the last question is answered, the panel swaps to a centred grade card with the score (e.g.
2/3) and theperfectorneedsWorkmessage —perfectonly renders when every question was correct on the first attempt. - Pass threshold.
passThreshold(default2) sets the minimum first-attempt correct count required to pass; the result is reported in thepassedfield handed toonComplete. - Streak badge. A
streak Nchip appears in the header once the student has two or more consecutive first-attempt correct answers; a wrong pick resets the streak to zero. - Wrong-hint slot. In retry mode, an optional
wrongHint(string or function ofwrongAttempts) renders between the question and the options after the first miss. - Reduced motion. Option ring transitions, the explanation enter / exit, the
Next questionbutton, and the wrong-hint banner all collapse to instant fades underprefers-reduced-motion: reduce. Scoring and locking still apply; only the motion drops.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
questions | readonly SynthesisQuizQuestion[] | required | List of questions in display order. |
doneMessages | { perfect, needsWork } | required | Messages for the synthesis grade screen. |
retryOnWrong | boolean | true | Bounce wrong picks back so the student can retry. |
wrongHint | string | (n: number) => string | — | Hint shown after a wrong attempt in retry mode. |
passThreshold | number | 2 | First-attempt correct count required to pass. |
tone | "default" | "accent" | "success" | "warning" | "error" | "accent" | Tone for the selected-option ring and the scenario badge. |
onCorrect | (idx, streak) => void | — | Fires after every correct answer. |
onWrong | (idx, attempts) => void | — | Fires after every wrong answer. |
onComplete | (score: SynthesisQuizScore) => void | — | Fires once the last question is answered. |
transition | Transition | SPRINGS.smooth | Override option / explanation / button transitions. |
className | string | — | Merged onto the outer <div> via cn(). |
Accessibility
- The option list is a
role="radiogroup"labelled by the question; each option is arole="radio"button witharia-checkedreflecting selection. - Every option is keyboard activated — Tab to focus, Space or Enter to pick — and renders a visible focus ring through
focus-visible:ring-cb-accent. - All interactive targets enforce a 44 x 44px minimum hit area (per WCAG 2.5.8 AAA) regardless of label width.
- The explanation banner, the wrong-hint banner, and the synthesis grade screen all use
role="status"+aria-live="polite"so a pick announces without stealing focus. - Options expose
data-state(rest/selected/correct/wrong); the panel exposesdata-state(active/done) anddata-perfecton the grade screen so consumer apps can hook custom styles or assistive tooling. - Motion respects
prefers-reduced-motion: reduce— option ring transitions, the explanation banner, the next-question button, and the wrong-hint banner collapse to instant fades.
Credits
- Extracted from:
algoflashcards(src/lessons/primitives/interaction/SynthesisQuiz.tsx). The source bounduseMultiQuizto auseLessonContext+trackHexaccent, theLessonButton.Pillatom, a per-questionVisualslot, theQuizDoneScreen+TrophyRevealcelebration components, and the project-levelplaySoundchannel. The library extract collapses the hook + multi-atom composition into a single self-contained component, drops the lesson-context dependency in favour of an explicitpassThreshold+onCompletecallback, swaps the inline track accents for thetonetoken ramp, and replaces the trophy celebration with an inline grade card. First-attempt scoring is preserved verbatim — only a question solved on the first try counts toward the synthesis grade.