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

Usage

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

  1. 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."
  2. 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.
  3. Controlled + uncontrolled. Pair value with onValueChange for fully controlled use (typical when narration steps drive the pointer), or pass defaultValue alone for a free scrubber.
  4. Tone drives every color. tone selects a token-driven color for both the marker fill and the floating label, so themes propagate without prop overrides.
  5. Cell stride. cellSize (default 32px) is the width of each cell and the snap stride. Match it to the array-row underneath so the pointer aligns visually with cells.
  6. 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.
  7. Reduced motion. Under prefers-reduced-motion: reduce, the snap transition collapses to duration: 0 — the pointer still moves, but instantly.

Props

PropTypeDefaultDescription
lengthnumberrequiredNumber of cells / valid pointer positions.
valuenumberControlled cell index in 0..length-1.
defaultValuenumber0Uncontrolled initial index.
onValueChange(value: number) => voidFired with the new index after drag / keyboard.
labelstring""Short label above the marker (e.g. "i", "mid").
tone"default" | "accent" | "success" | "warning" | "error""accent"Semantic color.
cellSizenumber32Cell width in pixels — also the snap stride.
disabledbooleanfalseDisables drag + keyboard interaction.
ariaLabelstringinferredOverride the accessible name.
classNamestringMerged 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, and aria-valuenow are wired so assistive tech reads the position out loud.
  • The default aria-label is "<label> pointer at index <n>" (or "Pointer at index <n>" when no label is given). Override via ariaLabel.
  • Hairline ticks and the floating label are marked aria-hidden="true" — the slider role already announces the value.
  • Drag animations respect prefers-reduced-motion: reduce and 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.