Tempo Control
An editable BPM input with tap tempo, keyboard stepping and a rolling average.
Installation
$ pnpm dlx shadcn@latest add @audiocn-solid/tempo-controlUsage
import { TempoControl } from "@/components/ui/tempo-control";<TempoControl defaultValue={120} onValueChange={setBpm} />Tap at least twice to measure a tempo. The control averages the last four intervals and starts a new sequence after more than two seconds without a tap. Typing or stepping the input clears the tap history.
BPM values are always whole numbers. Tapped, typed and externally supplied fractional values round to the nearest integer. The default range is 30-600 BPM; set min and max to suit your application.
Anatomy
The input and tap button render by default. Supply children to arrange the parts, change their labels, or omit the tap button.
<TempoControl>
<TempoControlInput />
<TempoControlTap>Tap tempo</TempoControlTap>
</TempoControl>Examples
Custom elements
Use TempoControlTap's as prop to replace the default trigger with a custom element. This example combines a sound pad with a tempo readout.
Controlled BPM
value remains owned by the parent. onValueChange reports changes with a reason of tap, keyboard or input. onValueCommitted fires after a measured tap, keyboard step, or text edit committed by blurring the input.
Sizes and disabled states
size and disabled inherit from AudioConfigProvider; explicit props win. readOnly keeps the input focusable but prevents editing and tapping.
Theming
| Variable or attribute | Meaning |
|---|---|
--tempo-height | Shared input and tap-button height, set by size |
data-size | sm, default or lg, on the root |
data-disabled | Disabled state, on the root |
data-readonly | Read-only state, on the root |
data-slot | tempo-control, tempo-control-input-group, tempo-control-input, tempo-control-unit, tempo-control-tap |
Use class on the root, input and tap button to customize the layout and appearance. Colours follow the standard shadcn theme tokens.
Accessibility
- The input is labelled "Tempo in beats per minute". Override
aria-label, supplyaria-labelledby, or pairidwith a visible label and remove the default witharia-label={undefined}. - Space and Enter activate the focused tap button.
- Disabled and read-only controls ignore taps. Holding a key does not generate repeated taps.
- Press feedback respects reduced-motion preferences.
<label for="delay-tempo">Delay tempo</label>
<TempoControl>
<TempoControlInput id="delay-tempo" aria-label={undefined} />
<TempoControlTap />
</TempoControl>| Focus | Key | Action |
|---|---|---|
| Input | Up / Down | Change by 1 BPM |
| Input | Shift + Up / Down | Change by largeStep |
| Input | Home / End | Set min / max |
| Tap button | Space / Enter | Tap once |
API reference
TempoControl
Prop
Type
TempoControlInput
Accepts Solid input DOM props, including id, aria-label, class and ref.
TempoControlTap
Accepts Solid button DOM props, including as, children, class, ref and disabled. A disabled or read-only root always disables tapping. An onClick handler can cancel a tap with event.preventDefault().
Text stays a local draft while editing, including empty or incomplete text. Blur or Enter restores the accepted rounded, clamped value. A controlled parent may reject an edit; committing restores its current value.