String Cells
A monospace grid that renders one character per cell — the canonical view for sliding-window, two-pointer, palindrome, and substring algorithms. Sibling of ArrayCells, specialized for strings: whitespace shows as a visible escape glyph, the font is locked to --cb-font-mono, and per-cell styles model the four states most string algorithms touch.
String of 7 characters.
r
0L
a
1c
2e
3c
4R
a
5r
6Customize
String
racecar
50%
Cell
36px
Installation
npx shadcn@latest add https://craftbits.dev/r/string-cells.jsonUsage
import { StringCells } from "@craft-bits/core";
<StringCells
value="racecar"
pointers={[
{ index: 0, label: "L", tone: "accent" },
{ index: 6, label: "R", tone: "accent" },
]}
/>Highlight the active window of a sliding-window scan:
<StringCells
value="abcabcbb"
pointers={[
{ index: 2, label: "L", tone: "success" },
{ index: 5, label: "R", tone: "warning" },
]}
cellStyles={{
0: "dimmed",
1: "dimmed",
2: "highlight",
3: "highlight",
4: "highlight",
5: "highlight",
}}
/>Understanding the component
- One cell per code point.
Array.from(value)splits the string by Unicode code points so emoji and combining characters get one cell each instead of breaking into surrogate halves. - Whitespace is visible. Space renders as
␣, tab as→, and newline as↵. The escape glyph paints in--cb-fg-subtleitalics so the visual weight differs from real characters. - Pointers above, indices below. Pointer labels animate above their cell via
AnimatePresence mode="popLayout"; the numeric index strip below usestabular-numsso digits don't wobble as labels change. - Per-cell style states. Each cell reads
cellStyles[i]—default,highlight,visited, ordimmed. The style maps to a semantic-token combo, so theming the accent through CSS variables repaints every cell. - Pointer tones. Pointers carry a
tonethat resolves to the matching--cb-*variable — the same colour adapts across light / dark / branded themes without a code change. - Responsive width. Cell width uses
clamp(24px, …, cellSize)so the row shrinks gracefully on narrow viewports and never breaks below the legibility floor. - Reduced motion. When
prefers-reduced-motion: reduceis set, the cell colour transition collapses toduration: 0— state changes snap instead of spring.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | required | The string to visualize. Split per Unicode code point. |
pointers | readonly StringCellPointer[] | [] | Pointer markers above the cells. Multiple may share an index. |
cellStyles | Record<number, StringCellStyle> | {} | Per-cell visual emphasis: default | highlight | visited | dimmed. |
cellSize | number | 32 | Ideal pixel width of each cell (cells shrink to fit narrow viewports). |
showIndices | boolean | true | Render the numeric index strip beneath the cells. |
caseSensitive | boolean | true | When false, glyphs are uppercased before rendering (the underlying value is untouched). |
className | string | — | Merged onto the outer <div>. |
Accessibility
- The outer element is
role="figure"with a visually hidden caption announcing the string length. - Each cell carries
role="img"and anaria-labellike"Index 3: c"(or"Index 4: space"for escaped whitespace) so a screen-reader user can read the string character-by-character. - Pointer tones derive from semantic-token CSS variables so users with custom palettes get the same contrast.
- The colour transition between cell states respects
prefers-reduced-motionand collapses to an instant swap when the user opts out. - Colour is never the only signal — every cell renders its character and a
data-stateattribute, so styling state stays readable for colourblind users.
Credits
- Extracted from:
algoflashcards(src/lessons/primitives/viz/StringCells.tsx). The library version normalises the fixed five-state palette to the four general-purpose states most string-algorithm visualisations need and folds the original siblingPointerLabelscomponent into the file so consumers install a single primitive.