Rewind Button

A rewind / reset transport control. Each click fires onRewind and spins the counter-clockwise arrow glyph one full turn — a short, satisfying signal that "we went back to the start." Three visual variants (solid, ghost, outline), three sizes (sm, md, lg), and an optional label that flips the shape from a circle to a pill.

Customize
Style
solid
md
Content

Installation

npx shadcn@latest add https://craftbits.dev/r/rewind-button.json

Usage

Icon-only — the simplest form, a circular button:

import { RewindButton } from "@craft-bits/core";
 
<RewindButton onRewind={() => setStep(0)} />

With a visible label — the button becomes a pill:

<RewindButton label="Restart" onRewind={() => reset()} />

Understanding the component

  1. Stateless action, not a toggle. Rewind is one-shot — there is no controlled / uncontrolled pattern because there is no on-off state to mirror. Each click is its own event; the only internal state is a turn counter that drives the icon spin.
  2. Strictly-decreasing rotation. The icon's rotate target is the turn count times -360deg, so each click moves Motion from the current angle to one full turn lower. The arrow never snaps back to zero between clicks, which would feel jittery; instead it keeps unspooling counter-clockwise on every press.
  3. Counter-clockwise glyph. The arrow is drawn as a three-quarter ring with the head pointing back into the curve. Counter-clockwise reads as "go back" the way clockwise reads as "go forward" — the direction of the spin and the direction of the glyph reinforce each other.
  4. CVA recipe with a compound-variant pill. rewindButtonVariants carries three visual variants, three sizes, and a withLabel slot. When label is provided, the compound variants switch the button from a fixed-width circle to a horizontal-padded pill so the label has room to breathe.
  5. Accessible by default. The rendered <button type="button"> always carries an aria-label — defaulting to the visible label when one is present, otherwise to "Rewind". The icon spin respects prefers-reduced-motion through Motion's spring fallback.

Variants

<RewindButton variant="solid" />
<RewindButton variant="ghost" />
<RewindButton variant="outline" />

Sizes:

<RewindButton size="sm" />
<RewindButton size="md" />
<RewindButton size="lg" />

With a label — switches to a pill:

<RewindButton label="Restart" />

Disabled — drops the tap gesture and the click handler:

<RewindButton disabled />

Props

PropTypeDefaultDescription
onRewind(event: MouseEvent) => voidFired when the user activates the button.
labelstringOptional visible label. When present the button becomes a pill.
variant'solid' | 'ghost' | 'outline''solid'Visual style of the button shell.
size'sm' | 'md' | 'lg''md'Touch-target diameter (32 / 40 / 48 px).
disabledbooleanfalseDisables the button and drops the tap gesture.
aria-labelstringlabel or 'Rewind'Override the accessible label.
classNamestringMerged onto the rendered <button> via cn().
...restHTMLMotionProps<'button'>Any other motion.button prop.

Accessibility

  • Renders a real <button type="button"> with an explicit aria-label — screen readers always announce the action even when the button is icon-only.
  • Keyboard activation: Enter and Space fire onRewind, same as any native <button>.
  • Focus is visible via a focus-visible: ring keyed to --cb-accent so keyboard users always see the current target.
  • Minimum hit area: every size meets Fitts ≥ 32×32 (sm = 32, md = 40, lg = 48).
  • The icon spin respects prefers-reduced-motion: Motion's spring transition collapses to an instant orientation change when the user has the OS-level preference set.
  • Color contrast: solid places --cb-accent-fg on --cb-accent; outline uses --cb-fg on --cb-bg-elevated; both pass WCAG AA in the default theme.

Credits

  • Extracted from: algoflashcards (src/lessons/primitives/interaction/RewindButton.tsx). The source path was empty — craft-bits implements the component from the concept as a CVA-driven primitive with three variants, three sizes, an icon-only / pill compound-variant slot, and a Motion-driven counter-clockwise spin keyed to a turn counter.