Bug Hunt Fix Phase
The second half of a BugHunt. After the learner has located the bug, this presents a small set of candidate fixes and reveals the correct one with per-option explanations.
Preview
Which fix is correct?
Customize
Options
Installation
npx shadcn@latest add https://craftbits.dev/r/bug-hunt-fix-phase.jsonUsage
import { BugHuntFixPhase } from "@craft-bits/core";
<BugHuntFixPhase
options={[
{
id: "lt-equals",
code: "for (let i = 0; i < nums.length; i++)",
correct: true,
explanation: "Stops the loop one index earlier.",
},
{
id: "minus-one",
code: "for (let i = 0; i <= nums.length - 1; i++)",
correct: false,
explanation: "Equivalent, but harder to read.",
},
]}
explanation="The loop guard is the bug — visit every index exactly once."
/>Controlled — drive selectedId and revealed from a parent reducer:
<BugHuntFixPhase
options={options}
selectedId={state.selectedFixId}
revealed={state.fixRevealed}
onSelect={(opt) => dispatch({ type: "select-fix", id: opt.id })}
/>Anatomy
- Question label — a small centered caption above the options (defaults to "Which fix is correct?").
- Option stack — each option is a radio button in a
radiogroup. Code snippets render in a small monospace tile; plain options render thelabelslot. - Reveal — once a pick is made (or
revealedflips totruein controlled mode), correct options paint green, the picked-wrong option paints red and shakes, untouched options dim. - Phase explanation — a tinted card below the options with an accent left-border. Color via
accentColor; defaults tovar(--cb-accent). - Per-option caption — the selected option's own
explanationrenders as a small caption beneath the phase explanation, ideal for distractor-specific feedback.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
options | readonly BugHuntFixOption[] | — | Candidate fixes. Exactly one should be correct: true. |
selectedId | string | null | — | Controlled selection. Omit for uncontrolled. |
defaultSelectedId | string | null | null | Initial selection when uncontrolled. |
onSelect | (option) => void | — | Fires whenever a selection is made. |
revealed | boolean | — | Controlled reveal state. Omit to flip on first pick. |
onReveal | (option) => void | — | Fires on the first pick when uncontrolled. |
question | ReactNode | "Which fix is correct?" | Label above the option stack. |
explanation | ReactNode | — | Phase-level explanation rendered after reveal. |
accentColor | string | var(--cb-accent) | Left-border accent for the explanation card. |
className | string | — | Merged onto the root via cn(). |
Accessibility
- The option stack is a
role="radiogroup"with anaria-label; each option is arole="radio"button witharia-checkedmirroring the selection. - Disabled-after-reveal is communicated via the native
disabledattribute so screen-reader users know further taps are inert. - Both explanation slots render in
role="status"regions and are announced politely as they appear. - A
:focus-visiblering replaces the default outline so keyboard users can step through the options with Tab and activate with Enter or Space.
Credits
- Extracted from:
AlgoFlashcards(src/lessons/primitives/BugHunt/BugHuntFixPhase.tsx). Stripped theuseBugHuntContextcoupling (phase / state / actions / trackHex / fixOptions all came from a lesson-level context), reshaped the option API from acorrect: booleanarray indexed by position into a stableid-keyedBugHuntFixOption[]so options can be re-ordered or hot-swapped without losing selection state, and replaced the hardcodedring-foreground/*andbg-success/NTailwind classes withcb-*semantic tokens.