Linked List Viz
A horizontal singly-linked-list visualization. Renders value-bearing circular nodes connected by directed arrows, with labeled pointers above (slow, fast, prev, curr) and a trailing ∅ terminator. Built on the shared SvgDefs arrow markers and the cb-* token system — there are no inline hex colors.
Customize
Shape
5
1
Pointer
slow
accent
Installation
npx shadcn@latest add https://craftbits.dev/r/linked-list-viz.jsonUsage
import { LinkedListViz } from "@craft-bits/core";
<LinkedListViz
nodes={[
{ id: "n1", value: 3 },
{ id: "n2", value: 7 },
{ id: "n3", value: 1 },
{ id: "n4", value: 4 },
]}
pointers={[
{ nodeId: "n2", label: "slow", tone: "accent" },
{ nodeId: "n4", label: "fast", tone: "warning" },
]}
nodeStyles={{ n2: "highlight", n4: "highlight" }}
showHead
/>Reverse-list scaffolding — three pointers, mid-iteration:
<LinkedListViz
nodes={[{ value: 1 }, { value: 2 }, { value: 3 }, { value: 4 }]}
pointers={[
{ nodeId: 0, label: "prev", tone: "error" },
{ nodeId: 1, label: "curr", tone: "accent" },
{ nodeId: 2, label: "next", tone: "success" },
]}
nodeStyles={{ "0": "visited", "1": "highlight" }}
/>Understanding the component
- SVG-based, single layer. The whole viz is a single
<svg>— nodes are<circle>+<text>, edges are<motion.line>with the shared arrow marker. No HTML overlay, noforeignObject. The viewBox auto-sizes fromnodes.length,nodeSize, andnodeSpacing. - Shared
SvgDefs. Arrow markers come from<SvgDefs>(the canonical defs block), not local<marker>declarations. Thehex="currentColor"binding lets the parent'stext-cb-*class drive the marker color. - Four node styles.
nodeStyles[id]selects from"default"(neutral elevated surface),"highlight"(accent ring + accent-muted fill — the "current" node),"visited"(muted, no glow), and"dimmed"(low-opacity ghost for unreachable / lookahead). - Pointer labels are SVG groups. Each pointer is a
<motion.g>with the label text + a small tick mark. The group'sxanimates withSPRINGS.smoothso the label glides between nodes — no AnimatePresence remounts on move. - Null terminator. When
nullTerminated(defaulttrue), a small arrow +∅glyph render after the last node. This is the canonical "next === null" marker; it makes the list visually closed. - Reduced motion.
usePrefersReducedMotion()short-circuits every transition to{ duration: 0 }— pointer moves, node entries, and arrow draws all snap into place.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
nodes | readonly { value: string | number; id?: string | number }[] | required | Ordered list of nodes (left → right). |
pointers | readonly { nodeId; label; tone? }[] | — | Labels above target nodes (slow, fast, prev, …). |
nodeStyles | Record<string, "default" | "highlight" | "visited" | "dimmed"> | — | Per-node visual override keyed by node id (or index string). |
showHead | boolean | false | Render a head marker arrow before the first node. |
showTail | boolean | false | Render a tail marker arrow below the last node. |
nullTerminated | boolean | true | Append the ∅ terminus after the last node. |
nodeSize | number | 40 | Diameter in pixels. |
nodeSpacing | number | 80 | Horizontal spacing between node centers. |
className | string | — | Merged onto the outer <svg>. |
Accessibility
- The outer
<svg>isrole="img"with anaria-labelthat flattens the list to a screen-readable string ("Linked list: 3 -> 7 -> 1 -> 4 -> null"). - Decorative glyphs (the
∅terminus, pointer ticks, head / tail labels) are visual chrome — the readable narrative lives inaria-label. - Color is never the only signal — every node renders its numeric or string value, and pointer labels carry their own text (
"slow","prev", …), so the viz stays legible for colorblind users and at every theme setting. - Motion respects
prefers-reduced-motion: pointer moves and node entries collapse to instant when the user has opted out.
Credits
- Extracted from:
algoflashcards(src/lessons/primitives/viz/LinkedListViz.tsx). The original was a rich reversal-lesson widget bundled with shatter particles, sever-point geometry, ghost trails, and an unreachable-bracket overlay — the library extract is the linked-list primitive itself: nodes, arrows, pointers, head / tail markers, and a null terminus.