Inline Array

A compact array-as-chip primitive. Renders a flat (number | string)[] as a single bordered strip of cells sized to flow inline with the surrounding text — the right glyph for prose like "the frontier is now …" or "we've seen …" where a full-width array visualization would be overkill.

Cells fade-and-pop in when added, fade out when removed, and slide between positions when the array re-orders. Optional highlighted indices and a paired highlightTone mark the cell the algorithm is currently touching.

Frontier Array, 3 elements: 1, 2, 3.123 grows by one each tick.

Customize
Shape
6
accent
success
Playback
900 ms

Installation

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

Usage

import { InlineArray } from "@craft-bits/core";
 
<p>
  The frontier so far is <InlineArray values={[1, 2, 3]} />.
</p>

Mark the head of the array with a contrasting tone:

<InlineArray values={[1, 2, 3, 4]} highlighted={[3]} highlightTone="success" />

Pin a stable key when the array contains duplicates so re-orders animate cleanly:

<InlineArray
  values={frontier.map((node) => node.label)}
  getKey={(_, i) => frontier[i].id}
/>

Render with no motion (e.g. inside non-animated copy):

<InlineArray values={[3, 6, 1, 4]} animate={false} />

Understanding the component

  1. One bordered strip. The outer span carries the array's border, background tint, and border-radius. Each cell is an inline-flex child that paints its own color and divider — so the whole array reads as a single chip in running text.
  2. Inline sizing. The strip is h: 1.55em and uses align-middle with tabular-nums so digits stay column-aligned and the chip's baseline matches the surrounding line of text. Resize the prose and the chip resizes too.
  3. Tone via CSS variables. tone picks a cb-* semantic var (accent / success / warning / danger / muted); the border, fill, and ink all derive from that one variable via color-mix(…) so the chip themes correctly under light and dark mode.
  4. Highlighted cells. Indices in highlighted switch to highlightTone (defaults to the resting tone). A highlighted cell gets a stronger tinted fill on top of the shared strip background — useful for marking the head, the cursor, or the last touched index.
  5. Stable keys. By default the cell's React key is String(value) — fine for unique-valued arrays. Arrays with duplicates should pass getKey so re-orders animate as motion instead of unmount/remount.
  6. Entry, exit, reorder. Cells wrap in AnimatePresence mode="popLayout" so additions pop in, removals fade out, and reorders glide (via Motion's layout prop). initial={false} skips the mount-time stagger.
  7. Empty state. Pass an empty array and the chip renders an empty-set glyph ({ }) at the same size — the right rest state for an array that "hasn't been built yet."
  8. Reduced motion. When prefers-reduced-motion: reduce is set, the cell transition collapses to duration: 0 — additions and removals snap.

Variants

A static array in prose:

<p>
  Sort <InlineArray values={[5, 2, 8, 1]} /> by ascending key.
</p>

A typed-token array with mixed strings:

<InlineArray values={["a", "b", "c"]} tone="muted" />

A growing frontier with a pulsing head:

<InlineArray
  values={[0, 1, 1, 2, 3]}
  highlighted={[4]}
  highlightTone="warning"
/>

The empty state — useful as the initial render before the array is built:

<InlineArray values={[]} />

Props

PropTypeDefaultDescription
values(number | string)[]requiredValues rendered inside each cell, left → right.
highlightednumber[]Indices to render in highlightTone instead of the resting tone.
toneInlineArrayTone"accent"Resting tone — accent | success | warning | danger | muted.
highlightToneInlineArrayTonesame as toneTone for highlighted cells.
getKey(value, index) => string | numberString(value)Stable React key per cell — pass for duplicate-valued arrays.
emptyPlaceholderstring"{ }"Glyph rendered when values is empty.
animatebooleantrueSet false to disable enter/exit motion (snap transitions).
transitionTransitionSPRINGS.snapOverride the cell transition.
classNamestringMerged onto the root via cn().

Accessibility

  • The outer element is role="img" with a hidden summary like Array, 4 elements: 1, 2, 3, 4. so screen readers hear the array's contents instead of reading each cell's digit in isolation.
  • Each cell carries data-tone (the active tone) and data-highlight (true / false) so consumer apps can hook custom styles or assistive tooling.
  • The empty state renders the placeholder glyph alongside an aria-label="Empty array" so the chip stays meaningful when it has no content.
  • Color is never the only signal — highlighted cells get an extra tinted fill on top of the shared strip background, and the value's digit is the primary cue regardless of tone.
  • Motion respects prefers-reduced-motion: reduce — cells snap in and out instead of springing.

Credits

  • Extracted from: craftingattention (app/src/components/ui/richtext-array.tsx). The source parsed a literal text string ([3,6], {1,2,3}, [[3,6],[1,4]]) and branched on three shapes plus an empty-set glyph; the library extract drops the parser and accepts a typed values: (number | string)[] so the same component covers any array source — typed arrays, sets, frontiers, paths — without round-tripping through string. The 2D-array branch lives in lesson code as a <div> of InlineArrays; the inline-prose chip is the single primitive shipped here. The hardcoded success-green palette was replaced with the cb-* tone system so the chip themes correctly under any palette.