Prediction Gate With Hints
A multi-option prediction checkpoint with an integrated, progressive hint ladder. The student commits to a pick; wrong picks shake and badge red but stay tappable, and the student can reveal one hint at a time between attempts. The gate locks the moment a correct answer lands, and the answer event records the number of attempts and hints used so the parent can weight scoring.
Which traversal visits the root last?
0 / 3 hints
- Pick an option or reveal a hint to log an event.
Installation
npx shadcn@latest add https://craftbits.dev/r/prediction-gate-with-hints.jsonUsage
import { PredictionGateWithHints } from "@craft-bits/core";
<PredictionGateWithHints
question="Which traversal visits the root last?"
options={[
{ id: "pre", label: "Pre-order" },
{ id: "in", label: "In-order" },
{ id: "post", label: "Post-order" },
{ id: "lvl", label: "Level-order" },
]}
correctId="post"
hints={[
"It is the only depth-first order where the root prints after its subtrees.",
"The name refers to where the root sits in the visit sequence.",
"Post-order: visit left, then right, then the root.",
]}
onAnswer={(e) => console.log(e.id, e.correct, e.attempts, e.hintsUsed)}
onHintRequest={(level) => console.log("hint", level)}
/>Hints are optional — omit the prop for a pure prediction gate:
<PredictionGateWithHints
question="Pick the tighter bound."
options={[
{ id: "a", label: "O(n)" },
{ id: "b", label: "O(n log n)" },
]}
correctId="a"
onAnswer={handleAnswer}
/>Understanding the component
- Single-shot once correct. The gate ignores all input after the first correct pick. Wrong picks badge red, shake, and stay tappable so the student can keep trying — no parent state required.
- Progressive hints. The hint ladder reveals one rung at a time via a
Show a hintbutton. Each reveal firesonHintRequest(level)so the parent can score how cheaply the answer was earned. - Answer event carries the cost. Every
onAnswercall includesattempts(wrong picks before this answer) andhintsUsed(hints revealed at the moment of the answer). Both are zero when the student nails it cold. - Hints are optional. Omit the prop entirely and the hint UI disappears. The component falls back to a plain multi-option prediction gate.
- Theme-driven colours. Buttons paint through
cb-accent,cb-success, andcb-errortokens — swap the theme and every state repaints with no prop changes. Reduced-motion users get instant feedback with no shake or pop.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
question | ReactNode | required | Prompt rendered above the option buttons. |
options | readonly { id: string; label: ReactNode }[] | required | Ordered list of options (2-4 recommended). |
correctId | string | required | id of the correct option. |
hints | readonly ReactNode[] | — | Progressive hint ladder, revealed one rung at a time. |
onAnswer | (event) => void | required | Fired on every tap with { id, correct, attempts, hintsUsed }. |
onHintRequest | (level: number) => void | — | Fired when the student reveals a new hint (1-based). |
disabled | boolean | false | Force-disable taps without resolving. |
hintButtonLabel | ReactNode | "Show a hint" | Override the hint reveal button label. |
hintExhaustedLabel | ReactNode | "All hints shown" | Label once the hint ladder is exhausted. |
aria-label | string | "Prediction with hints" | Accessible name for the gate region. |
className | string | — | Merged onto the outer container. |
Accessibility
- The root is a
role="group"labelled by the question paragraph. Inner buttons share arole="radiogroup"so screen readers announce them as a paired choice. - Each option is a
role="radio"witharia-checkedset on the winning option. Disabled buttons drop from focus. - Every option carries an
aria-labelderived from a string label, falling back to the optionidfor non-string labels. - The hint list lives inside an
aria-live="polite"region so each newly revealed rung is announced without interrupting. - Tap feedback collapses to instant under
prefers-reduced-motion: reduce— no shake, no scale pop, no hint slide-in. - Option buttons clear the 44 x 44 px minimum touch target via
min-h-[44px]and horizontal padding.
Credits
- Extracted from:
algoflashcards(src/lessons/primitives/interaction/PredictionGateWithHints.tsx). The source was a thin wrapper that wired aPredictionGateto a separateHintLadderthrough ausePredictionGateStatereducer, took a hardcodedtrackHexcolour prop, and depended on per-distractor feedback maps + a project-wide sound-effect registry. craft-bits collapses to a single self-contained component with a flathints[]ladder, lifts the API tooptions+correctId+onAnswer+onHintRequest, and rewires every colour throughcb-accent/cb-success/cb-errortokens so theme swaps repaint without prop changes.