Queue Lane
A horizontal FIFO queue visualization. Items enter from the tail (right by default) and exit from the head (left by default), gliding between cells via motion's layoutId-driven layout animation. Common for teaching BFS frontiers, task scheduling, and message queues.
A
B
C
Customize
Lane
6
Installation
npx shadcn@latest add https://craftbits.dev/r/queue-lane.jsonUsage
import { QueueLane } from "@craft-bits/core";
<QueueLane
items={[
{ id: 1, label: "A" },
{ id: 2, label: "B", tone: "highlight" },
{ id: 3, label: "C" },
]}
/>Drive enqueue / dequeue from your own algorithm reducer — the component is pure render:
const [queue, setQueue] = useState<QueueLaneItem[]>([]);
<QueueLane items={queue} />
<button onClick={() => setQueue((q) => [...q, { id: nextId(), label: "X" }])}>
enqueue
</button>
<button onClick={() => setQueue((q) => q.slice(1))}>
dequeue
</button>Understanding the component
- Head-first input, head-first DOM. The
itemsarray is read head-first —items[0]is the head (next to leave),items.at(-1)is the tail (just arrived). The lane mirrors viaflex-row-reversewhendirection="enqueue-left", so the algorithmic convention stays the same regardless of visual orientation. layoutId-driven motion. Every cell carrieslayoutId={item.id}andlayout, so dequeue / enqueue animate as glides instead of remount-and-fade. New cells enter withtranslateX(±24) + scale(0.96) + opacity 0from the enqueue side; popped cells exit with the mirrored transform out the dequeue side.SPRINGS.smoothfor layout,RM_TRANSITIONfor reduced motion. All transitions resolve to one of two values: the smooth spring (default) or{ duration: 0 }whenprefers-reduced-motion: reduceis set. There are no inline timing objects.- Capacity overflow. When
capacityis set and the queue is longer, the lane shows the firstcapacityitems (head-side) and collapses the overflow into a single dashed+Nchip on the tail side. The view always shows what's about to leave — the most teachable end. - Per-item tone. Each
items[i].toneis one ofdefault,highlight, orvisited. Tones resolve to inline CSS-var styles (--cb-accent,--cb-fg-subtle,--cb-bg-muted) so consumer themes repaint the cells without a re-render. - End labels.
showLabelstoggles smallhead ←/→ tailmarkers at the ends of the lane. Arrows always point outward on the dequeue side and inward on the enqueue side — same visual grammar at both lane orientations.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
items | readonly QueueLaneItem[] | required | Queue contents, head-first. items[0] is the head, items.at(-1) is the tail. |
capacity | number | — | Cap on visible cells. Excess items collapse into a single +N chip on the tail side. |
direction | "enqueue-right" | "enqueue-left" | "enqueue-right" | Which end items enter from. |
showLabels | boolean | true | Render small head / tail markers at the ends of the lane. |
className | string | — | Merged onto the outer <div>. |
QueueLaneItem
| Field | Type | Description |
|---|---|---|
id | string | number | Stable React + layoutId key. Must be unique across the queue. |
label | ReactNode | Visible cell content. Strings and numbers narrate to assistive tech; richer JSX renders visually only. |
tone | "default" | "highlight" | "visited" | Per-item semantic tone. Defaults to "default". |
Accessibility
- The lane is
role="list"witharia-live="polite", and itsaria-labelis rebuilt on every render ("Queue head: A, tail: D. 4 items.") so SR users hear the FIFO state after every enqueue / dequeue. - Each cell is
role="listitem"and carriesdata-state="default" | "highlight" | "visited"so consumer apps can hook custom styles or testing selectors without parsing classes. - Head / tail markers and directional arrows are
aria-hidden="true"— the live region already names the head and tail, so duplicating them in the labels would announce twice. - Color is never the only signal: the
highlighttone adds an inset ring,visitedmutes the cell, and every cell carries its own text label. - Motion respects
prefers-reduced-motion: enter / exit / layout transitions collapse to{ duration: 0 }so the lane snaps.
Credits
- Extracted from:
algoflashcards(src/lessons/primitives/viz/QueueLane.tsx). The original threaded a per-trackhexcolor and AF-specificSPRING.snappy/STAGGER.tightconstants through every cell; the library extract narrows the API to semantic tones from the--cb-*token system and replaces the staggered entrance withlayoutId-driven layout animation so reorders glide instead of cascading.