Chain Rule Unpacker
Click-through chain-rule walkthrough for y = sin(x²). Two nested coloured boxes (outer = sin, inner = x²) split apart in sequence to reveal each layer's local derivative, which then slides down into a product strip that accumulates dy/dx = cos(x²) · 2x.
The component runs as a four-stage narrative — packed, outer-unpacked, inner-unpacked, complete — advanced by a single action button or by a controlling parent via the stage / onStageChange API. The walkthrough is the mental model behind backprop: every layer contributes its local derivative, and the chain rule multiplies them together along the path.
This function is a composition — sin wrapped around x². The outer function takes the inner function's output as its input. Unpack the outer layer to find its derivative.
Installation
npx shadcn@latest add https://craftbits.dev/r/chain-rule-unpacker.jsonUsage
import { ChainRuleUnpacker } from "@craft-bits/viz/chain-rule-unpacker";
<ChainRuleUnpacker />Drive the walkthrough from a parent:
const [stage, setStage] = useState(0);
<ChainRuleUnpacker stage={stage} onStageChange={setStage} />Render a custom action button instead of the built-in one:
<ChainRuleUnpacker
hideAction
renderAction={({ label, disabled, onClick }) => (
<MyButton onClick={onClick} disabled={disabled}>{label}</MyButton>
)}
/>Hide the narration when embedding inside an article that already explains the chain rule:
<ChainRuleUnpacker hideNarration />Understanding the component
- Composition as nested boxes. Stage 0 renders
sin( ... )as a wide outer box around a smallerx²inner box. Subtle role hints sit in the corners while the outer stroke breathes on a 3s loop. - Unpacking is a half-split. Each click splits the active box into two halves that slide apart on
SPRINGS.smooth, opening just enough room to reveal the local derivative between them. - Derivatives bounce in. Once the box halves are open, the derivative label fades in on
SPRINGS.bouncywith a one-shot glow pulse, then a copy slides down into the product strip onSPRINGS.smooth. - Product accumulates left to right. Stage 1 lands
cos(x²), stage 2 lands× 2x, stage 3 lands= dy/dxand flashes the strip green to mark completion. - Reset snaps back. Stage 3 → Stage 0 collapses the halves, hides the derivatives, and drains the product strip on
SPRINGS.snapso the loop can start again. - Reduced motion. Under
prefers-reduced-motion: reduce, every spring is replaced by an instant attribute set — the visual end-state of each stage still renders, but no motion plays.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
stage | 0 | 1 | 2 | 3 | — | Controlled stage. Pair with onStageChange. |
defaultStage | 0 | 1 | 2 | 3 | 0 | Initial stage for the uncontrolled API. |
onStageChange | (next) => void | — | Fires after the component commits to the next stage. |
transition | Transition | SPRINGS.smooth | Override the spring for box splits and label fades. |
actionLabels | Partial<Record<stage, string>> | — | Override the action-button label per stage. |
narrationCopy | Partial<Record<stage, string>> | — | Override the narration copy per stage. |
hideNarration | boolean | false | Hide the narration paragraph. |
hideAction | boolean | false | Hide the built-in action button. |
renderAction | ({ stage, label, disabled, onClick }) => ReactNode | — | Render slot for a custom action button. |
className | string | — | Merged onto the root via cn(). |
Accessibility
- The root carries
aria-labelledbypointing to a hidden<span>summary of the current stage ("Chain rule unpacker. Stage: outer unpacked..."), so screen readers hear the current narrative state without parsing the SVG. - The SVG itself is
role="img"with the samearia-label, so consumers who slot the SVG into another tree still surface a readable name. - An
aria-live="assertive"region under the SVG announces each stage transition ("Outer layer unpacked...", "Chain rule complete...") so non-sighted users hear when each new derivative lands. - The action button is a real
<button type="button">with:focus-visibleoutline,disabledstate during animation, and standard keyboard activation. - Status is never the only signal — every stage emits both an aria-live announcement and a visible label / derivative; colour signals (accent, warning, success) are reinforced by position and copy.
- Motion respects
prefers-reduced-motion: reduce— every spring collapses to an immediate attribute set, including the box-split, derivative reveal, glow pulse, and success flash.
Credits
- Extracted from:
craftingattention(app/src/lessons/primitives/math/ChainRuleUnpacker.tsx). The library extract drops the project-specificSvgLabelandChallengeBtnchrome in favour of plain<text>and acn()-styled<button>, swaps the per-tone--color-accent-*/--color-warn-*/--color-success-*palette for the canonical--cb-accent/--cb-warning/--cb-successtoken vars, replaces inline spring objects withSPRINGS.smooth/SPRINGS.snap/SPRINGS.bouncy, and adds a controlledstage/onStageChangeAPI plusrenderActionslot so consumers can drive the walkthrough from a parent stepper, a keyboard, or a custom button.