stepwise
v0.1.0 - MIT - React 18 & 19

Narrate any sequence.
One cursor, every scale.

stepwise is a React kit for interactive, narrated step-throughs: player-driven traces, guided tours, scroll-told lessons, and show-once feature tutorials. All of them run on one core primitive, a clamped cursor called useSteps.

release-pipeline - live playerplaying
$ npm run build
// esm 41.7 kB - dts clean
BuilduseStepPlayer - PlayerBar - CodeBlock
1/5
useStepPlayer()

timers, speed, deep links, engine port

useGuidedTour()

effects, guards, edges - a tour state machine

useChapters()

scroll to cursor, viewport-center containment

In code

What it looks like in your app

Each tab pairs the usage code with the component it renders, running live on the right.

pipeline-demo.tsx
const FRAMES = [
{ tone: "write", label: "Parse", code: ["tokens = lexer(src)"] },
{ tone: "write", label: "Reduce", code: ["ast = parser(tokens)"] },
{ tone: "good", label: "Done", code: ["return ast"] },
];
function PipelineDemo() {
const player = useStepPlayer({
total: FRAMES.length,
autoPlay: true,
onEnd: () => console.log("done"),
});
const frame = FRAMES[Math.max(0, player.index)];
return (
<>
<CodeBlock lines={frame.code} />
<PlayerBar player={player} />
{player.ended && <DoneBadge />}
</> );
}
Resultlive
tokens = lexer(src)
1/3

frame 1/3 - Parse

Theming

Your design system, not ours.

Components ship className strings, but every color is either a CSS variable or a token in the injectable StepwiseTheme - surfaces, overlays, code panels, kbd chips, the spotlight. Swap in any design system without forking the kit.

Try it: the toggle in the header switches the whole site between the two palettes, theme tokens included. This code sample is the library's CodeBlock.

const light: StepwiseTheme = {
toneSegment: { good: "bg-blue-600", ... },
buttonVariants: {
primary: "bg-blue-600 text-white",
outline: "border-blue-600/40 text-slate-800",
},
surface: "border-slate-200 bg-white/90",
kbd: "border-slate-300 bg-slate-50",
muted: "text-slate-400",
};
<PlayerBar theme={light} ... /> // pass in your own theme

The hooks are headless; the styled components are optional. Use your own markup and keep the behavior.