Ruler slider

A tape of ticks that slides under a fixed needle, with momentum, snapping, and a rolling readout.

pnpm dlx shadcn@latest add @uiarc/ruler-slider
Live · keyboard ready
Height
120130140150160170180190200210220

That is 5 ft 8 in

Weight
3035404550556065707580859095100105110115120125130135140145150
  • Onboarding and profile flows that ask for body measurements or goals.
  • Mobile-first inputs where dragging a scale is easier than typing.
  • Wide ranges with fine steps that a short slider track cannot resolve.
  • Use number-field when people know the exact number and prefer to type it.
  • Use slider or elastic-slider when the value is relative, like volume.
  • Use range-histogram-slider to pick a range.

Installation

Add Ruler slider with the shadcn CLI, or copy the source by hand.

pnpm dlx shadcn@latest add @uiarc/ruler-slider

Adds the component and its local dependencies, and installs motion. First time? Add the @uiarc registry to components.json, or use the full URL:

example.tsx
import { RulerSlider } from "@/components/arc/ruler-slider/ruler-slider"; export function HeightField() {  const [height, setHeight] = useState(172);  return (    <RulerSlider      label="Height"      unit="cm"      min={120}      max={220}      value={height}      onValueChange={setHeight}      onValueCommit={saveHeight}    />  );}

RulerSlider

A precise value input shaped like a tape measure. A ruler of ticks slides under a fixed needle; drag it, flick it to coast onto a tick, tap to glide there, or scroll horizontally. The readout rolls each digit.

PropTypeDefaultDescription
labelRequiredstring–Visible label. It also names the control.
valuenumber–Controlled value. Leave it out for uncontrolled use.
defaultValuenumberminInitial value when uncontrolled.
onValueChange(value: number) => void–Called whenever a tick passes the needle, including while coasting.
onValueCommit(value: number) => void–Called once the tape comes to rest after a drag, flick, tap, scroll, or key.
minnumber0Lowest value.
maxnumber100Highest value.
stepnumber1Value of one small tick. The tape always rests on a tick.
largeStepnumber–Distance for PageUp, PageDown, and Shift with an arrow. Defaults to one labelled tick (step × majorEvery).
majorEverynumber10Small ticks between labelled ticks.
spacingnumber10Pixels between small ticks. Wider spacing gives finer control.
format(value: number) => string–Formats the readout, the tick labels, and aria-valuetext.
unitstring–Unit shown after the readout and spoken with the value, such as "cm".
namestring–Submits the value with a form through a hidden input.
disabledbooleanfalseBlocks input and removes the control from the tab order.
classNamestring–Extra class on the root.
ArrowRightorArrowUp
Moves one tick up.
ArrowLeftorArrowDown
Moves one tick down.
Shift+ArroworPageUporPageDown
Moves one labelled tick (largeStep).
HomeorEnd
Goes to min or max.
  • The tape viewport is role="slider" with aria-valuemin, aria-valuemax, aria-valuenow, and aria-valuetext that includes the unit.
  • It is labelled by the visible label through aria-labelledby.
  • The rolling readout and needle are aria-hidden; the slider announces the value.
  • Horizontal trackpad scrolling moves the tape; vertical scrolling is left to the page.
  • Dragging moves the tape 1:1; a flick coasts with scroll-like deceleration and lands on a tick.
  • Pulling past either end rubber-bands, and releasing pulls the tape home with the throw's speed.
  • Taps and keys glide on a spring that keeps any speed already in the tape; grabbing a coasting tape catches it in place.
  • Each digit of the readout rolls on its own column as ticks pass the needle.
  • Reduced motion removes coasting, rubber-banding, and glides; the tape jumps to the nearest tick.
  • The viewport fills its container and fades the tape at both edges; the needle stays centered at any width.
  • Touch drags, flicks, and taps work the same as mouse; small moves under 4px count as a tap.
  • Horizontal trackpad scrolling moves the tape without capturing vertical page scroll.
  • Ticks are drawn with CSS gradients, not one element per tick, so long ranges stay cheap.
  • Only labelled ticks are elements; keep (max - min) / (step × majorEvery) to a few hundred.
  • Tape position is a motion value, so dragging and coasting do not re-render per frame.

Notes for AI

Give your coding assistant the Markdown reference instead of screenshots.

  • Choose it for measurements and precise values on touch: height, weight, temperature, tip amount, zoom level.
  • Set step to the precision you store and majorEvery so labelled ticks land on round numbers (10 for cm, 5 or 10 for kg).
  • Increase spacing when people need finer control on small screens.
  • Use onValueCommit for saves: onValueChange fires on every tick while the tape coasts.

The full library index for assistants is at /llms.txt.