Slider Pointer
A scrubbable pointer marker that slides along a row of cell positions. The pointer's position is the value — drag it, or arrow-key it, to walk through indices 0..length-1. Teaches two-pointer techniques (i / j sliding), binary search (mid-pointer), and sliding-window algorithms.
Customize
Pointer
4
10
Appearance
accent
Installation
npx shadcn@latest add https://craftbits.dev/r/slider-pointer.jsonUsage
import { SliderPointer } from "@craft-bits/core";
<SliderPointer length={10} defaultValue={4} label="i" />Controlled, for two-pointer narration:
const [i, setI] = useState(0);
<SliderPointer length={8} value={i} onValueChange={setI} label="i" tone="accent" />Binary-search mid-pointer with a warning tone:
<SliderPointer length={12} defaultValue={5} label="mid" tone="warning" />Understanding the component
- Discrete cell snap. The pointer always lands on an integer index in
[0, length-1]. On drag release the marker snaps to the nearest cell via the snap spring — the tension reads as a confident "click into place." - Hairline cells. The track paints a single baseline rule plus one short tick per index so the pointer's position is unambiguous without competing with whatever array-cell rendering teachers layer on top.
- Controlled + uncontrolled. Pair
valuewithonValueChangefor fully controlled use (typical when narration steps drive the pointer), or passdefaultValuealone for a free scrubber. - Tone drives every color.
toneselects a token-driven color for both the marker fill and the floating label, so themes propagate without prop overrides. - Cell stride.
cellSize(default32px) is the width of each cell and the snap stride. Match it to the array-row underneath so the pointer aligns visually with cells. - Native keyboard a11y. A visually-hidden native range input sits over the track. Tab focuses, arrow keys step, Home / End jump to the bounds. Screen readers announce the slider role with current / min / max.
- Reduced motion. Under
prefers-reduced-motion: reduce, the snap transition collapses toduration: 0— the pointer still moves, but instantly.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
length | number | required | Number of cells / valid pointer positions. |
value | number | — | Controlled cell index in 0..length-1. |
defaultValue | number | 0 | Uncontrolled initial index. |
onValueChange | (value: number) => void | — | Fired with the new index after drag / keyboard. |
label | string | "" | Short label above the marker (e.g. "i", "mid"). |
tone | "default" | "accent" | "success" | "warning" | "error" | "accent" | Semantic color. |
cellSize | number | 32 | Cell width in pixels — also the snap stride. |
disabled | boolean | false | Disables drag + keyboard interaction. |
ariaLabel | string | inferred | Override the accessible name. |
className | string | — | Merged onto the outer <div>. |
Accessibility
- A visually-hidden native range input provides the slider role + keyboard handling — Tab focuses, Arrow-Left / Right step by one cell, Home / End jump to the bounds.
aria-valuemin,aria-valuemax, andaria-valuenoware wired so assistive tech reads the position out loud.- The default
aria-labelis"<label> pointer at index <n>"(or"Pointer at index <n>"when no label is given). Override viaariaLabel. - Hairline ticks and the floating label are marked
aria-hidden="true"— the slider role already announces the value. - Drag animations respect
prefers-reduced-motion: reduceand collapse to instant transitions.
Credits
- Extracted from:
algoflashcards(src/lessons/primitives/viz/SliderPointer.tsx). The original was a percentage-coupled, multi-marker decorative overlay tied to a continuous min/max range. The library version snaps to discrete cell indices, ships a single draggable marker with full keyboard support, and exposes a controlled / uncontrolled value contract so consumers can wire the pointer into two-pointer or sliding-window narration without reshaping their state.