Activation Match Inline
ActivationMatchInline is a compact drag-and-drop quiz that pairs deep-learning scenarios (a 96-layer transformer, a shallow CNN, a binary classifier, a multi-class head) with the activation each should ship with (GELU, ReLU, Sigmoid, Softmax). The learner drags an activation chip into a scenario card — or taps a chip to auto-place it into the first empty row — and once every row has an assignment, the Check answers button reveals correctness with a per-row pedagogical explanation.
The deck is data-driven: pairs defines the rows, the chip pool is derived from those rows (or supplied explicitly via activationChoices), and per-chip colors come from activationColors with a sensible default that maps the canonical four activations onto the --cb-info / --cb-accent / --cb-warning / --cb-success semantic tokens.
Installation
npx shadcn@latest add https://craftbits.dev/r/activation-match-inline.jsonUsage
import { ActivationMatchInline } from "@craft-bits/viz/activation-match-inline";
<ActivationMatchInline />Override the deck with your own pairs:
<ActivationMatchInline
pairs={[
{
id: "rnn",
scenario: "Recurrent layer in an LSTM",
detail: "Long sequence, gated cell state",
correctActivation: "Tanh",
explanation:
"Tanh keeps the cell state in (-1, 1) so it doesn't explode through time.",
},
{
id: "gate",
scenario: "Forget gate output",
detail: "Per-step retention probability",
correctActivation: "Sigmoid",
explanation:
"Sigmoid emits a (0, 1) gate — exactly the right shape for a per-step retention probability.",
},
]}
activationColors={{
Tanh: "var(--cb-accent)",
Sigmoid: "var(--cb-warning)",
}}
/>Listen for the score on submit:
<ActivationMatchInline
onCheck={({ score, total }) =>
analytics.track("activation_match", { score, total })
}
/>Anatomy
- Two interaction modes per chip. Activation chips can be dragged into any scenario card's drop zone, or clicked / Enter-pressed to auto-place into the first unassigned row. Reassigning an already-used chip moves it from its old row to the new one — there's always at most one chip per row and at most one row per chip.
- Always-visible drop zones. Unassigned scenarios render a "drop here" affordance that subtly tints when a chip is being dragged anywhere in the component. This keeps the drop target obvious without requiring a hover-over to surface it.
- Reveal-on-Check. While
revealedisfalse, the cards stay neutral and the chips stay interactive. Once Check Answers fires, every assigned card grows a 2px ring (--cb-successfor correct,--cb-errorfor wrong) and anAnimatePresenceheight-tween reveals the explanation block below. The chips become read-only. - Semantic color, never color-only. Correctness is also surfaced through the textual "correct" / "answer: <activation>" header and the score readout — color is supplementary, not the only signal.
- Reduced-motion respected throughout. Under
prefers-reduced-motion: reduce, the chip pop-in, the explanation height tween, the action-row enter / exit, and the chip hover/tap scales all collapse to zero-duration transitions.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
pairs | readonly ActivationMatchInlinePair[] | ACTIVATION_MATCH_INLINE_DEFAULT_PAIRS | Match rows. Each row's correctActivation must equal one of the available chips. |
activationChoices | readonly string[] | derived from pairs | The chip pool. When omitted, falls back to the unique correctActivation values in input order. |
activationColors | Readonly<Record<string, string>> | ACTIVATION_MATCH_INLINE_DEFAULT_COLORS | Map of activation name → CSS color. Missing entries fall back to --cb-accent. |
caption | string | — | Optional caption rendered under the action row. |
onAssignmentsChange | (next) => void | — | Called whenever an assignment changes. |
onCheck | ({ score, total, assignments }) => void | — | Called when Check Answers is tapped. |
onReset | () => void | — | Called when Try Again is tapped. |
className | string | — | Merged onto the root via cn(). |
Accessibility
- The scenarios live inside a
role="list"/role="listitem"pair; the chip pool is arole="group"with anaria-labeldescribing the interaction model. - Each chip is a focusable
role="button"with anaria-labelof the form"\<activation\> — drag to a scenario or press Enter to assign".Enter/Spacetriggers the auto-place behaviour. Used chips becomearia-disabledand lose tab-stoppability. - Each scenario card is
aria-labelledbyits scenario title and, once revealed,aria-describedbythe explanation block. - An off-screen
aria-live="polite"region announces progress ("N of M assigned") and the final score ("Score N of M") so screen-reader users get parity with the visual state. - Color is never the only correctness signal: every revealed row also carries the textual "correct" / "answer: <activation>" header, and the score readout switches between
--cb-successand--cb-warningplus the literalN/Mnumber. - Motion respects
prefers-reduced-motion: reduce— the chip pop-in, the explanation height tween, the action-row enter / exit, and the chip hover/tap scales all collapse to instant transitions.
Credits
- Extracted from:
craftingattention(app/src/lessons/primitives/viz/ActivationMatchInline.tsx). The source pulled inline--color-bar-4/--color-accent-400/--color-warn-400/--color-success-400/--color-fail-400/--color-ink-*/--color-surface-elevated/--color-accent-500palette references and aSPRINGS.snugspring. The viz extract re-keys them onto the--cb-*semantic-token vocabulary, swaps the spring forSPRINGS.snapfrom@craft-bits/core/motion, wraps everything inforwardRef+cn()+...propsspread, adds anactivationColorsknob, exposesonAssignmentsChange/onCheck/onResetcallbacks, narrows the drag MIME fromtext/plaintoapplication/x-cb-activation-match(with atext/plainfallback), and adds full reduced-motion handling plus a polite live region.