Array Cells

A horizontal row of equal-size labelled cells — the foundational primitive for any array-based DSA visualization. Each cell can be styled per-index (highlight / visited / dimmed), labelled above with its index, and tagged below with one or more pointer labels (L, R, mid, …).

Array, 8 cells, 2 pointers.
Customize
Shape
8
md
Static markers
0
4
Playback
600 ms

Installation

npx shadcn@latest add https://craftbits.dev/r/array-cells.json

Usage

import { ArrayCells } from "@craft-bits/core";
 
<ArrayCells
  values={[3, 8, 12, 17, 24, 31, 42, 55]}
  showIndices
  pointers={[
    { index: 0, label: "L", tone: "success" },
    { index: 7, label: "R", tone: "error" },
  ]}
  cellStyles={{ 3: "highlight" }}
/>

Drive cell state from your own algorithm reducer — the component is pure render:

<ArrayCells
  values={arr}
  pointers={[
    { index: left, label: "L", tone: "success" },
    { index: right, label: "R", tone: "error" },
    { index: mid, label: "mid", tone: "accent" },
  ]}
  cellStyles={Object.fromEntries(
    visited.map((i) => [i, "visited"] as const),
  )}
/>

Understanding the component

  1. A column per cell. Each cell is a vertical flex stack — index label on top, the cell box in the middle, and a pointer-label row underneath. The pointer row reserves space even when empty so the cell-box baseline doesn't jump as pointers move across the array.
  2. Responsive cell width. Cell width is clamp(min, calc((100% - totalGaps) / N), ideal) — cells render at cellSize on wide viewports but shrink to ~60% on narrow ones. Never overflows iPhone-sized screens; never expands past the ideal grid.
  3. Style states. cellStyles maps an index to one of four visual states: highlight (accent ring + tinted fill), visited (muted "done" look), dimmed (30% opacity), or default (resting look).
  4. Pointers carry tone. Each pointer has a semantic tone that drives both the pointer label color and the matching cell's ring. The first pointer on a cell wins the ring color; subsequent pointers stack as additional labels.
  5. Pointer transitions. Labels are wrapped in AnimatePresence with mode="popLayout" so moving a pointer between cells animates a fade-and-slide. initial={false} skips the mount-time stagger.
  6. Reduced motion. When prefers-reduced-motion: reduce is set, the transition collapses to duration: 0 — pointer state changes snap instead of springing.

Props

PropTypeDefaultDescription
values(string | number)[]requiredValues rendered inside each cell, left → right.
showIndicesbooleanfalseRender numeric indices above each cell.
pointers{ index, label, tone? }[][]Pointer labels rendered beneath cells.
cellStylesRecord<number, ArrayCellStyle>{}Per-cell state — default, highlight, visited, dimmed.
cellSizenumber44Ideal pixel size of each cell.
compactbooleanfalseTighter row, smaller cells, smaller labels.
classNamestringMerged onto the outer <div>.

Accessibility

  • The outer element is role="group" with an aria-label announcing the array length.
  • Each cell carries a data-state attribute (default / highlight / visited / dimmed) so consumer apps can hook custom styles or assistive tooling.
  • Indices are decorative — aria-hidden="true" so screen readers don't read out 0 3 1 8 2 12 ….
  • Color is never the only signal: highlighted cells get a ring (not just a fill), pointer labels carry text (L, R, mid), and dimmed cells drop their opacity — three orthogonal cues for colorblind users.
  • Pointer label motion respects prefers-reduced-motion: reduce.

Credits

  • Extracted from: algoflashcards (src/lessons/primitives/viz/ArrayCells.tsx). The original supported tap-to-select and per-cell hex overrides for lesson-specific theming; the library extract narrows the API to semantic tones + style states so the same component drops into any DSA viz unchanged.