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.json

Usage

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

  1. Head-first input, head-first DOM. The items array is read head-first — items[0] is the head (next to leave), items.at(-1) is the tail (just arrived). The lane mirrors via flex-row-reverse when direction="enqueue-left", so the algorithmic convention stays the same regardless of visual orientation.
  2. layoutId-driven motion. Every cell carries layoutId={item.id} and layout, so dequeue / enqueue animate as glides instead of remount-and-fade. New cells enter with translateX(±24) + scale(0.96) + opacity 0 from the enqueue side; popped cells exit with the mirrored transform out the dequeue side.
  3. SPRINGS.smooth for layout, RM_TRANSITION for reduced motion. All transitions resolve to one of two values: the smooth spring (default) or { duration: 0 } when prefers-reduced-motion: reduce is set. There are no inline timing objects.
  4. Capacity overflow. When capacity is set and the queue is longer, the lane shows the first capacity items (head-side) and collapses the overflow into a single dashed +N chip on the tail side. The view always shows what's about to leave — the most teachable end.
  5. Per-item tone. Each items[i].tone is one of default, highlight, or visited. 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.
  6. End labels. showLabels toggles small head ← / → tail markers 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

PropTypeDefaultDescription
itemsreadonly QueueLaneItem[]requiredQueue contents, head-first. items[0] is the head, items.at(-1) is the tail.
capacitynumberCap 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.
showLabelsbooleantrueRender small head / tail markers at the ends of the lane.
classNamestringMerged onto the outer <div>.

QueueLaneItem

FieldTypeDescription
idstring | numberStable React + layoutId key. Must be unique across the queue.
labelReactNodeVisible 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" with aria-live="polite", and its aria-label is 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 carries data-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 highlight tone adds an inset ring, visited mutes 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-track hex color and AF-specific SPRING.snappy / STAGGER.tight constants through every cell; the library extract narrows the API to semantic tones from the --cb-* token system and replaces the staggered entrance with layoutId-driven layout animation so reorders glide instead of cascading.