Skip to content
buttery

v1.0.0

Source on GitHub

Version 1.0.0

Slider

A labeled slider row: the label and its value badge beside a track you drag, click or step with the keyboard.

import { Slider } from "@butteryui/react";

Demo

Opacity40%

When to use it

Use it for

  • A continuous value you judge by eye: rotation, scale, blur, opacity
  • Inspector rows, where the live value shows in the badge

Not for

  • An exact number people type: use NumberField
  • A few named levels: use SegmentedControl

Examples

Beside a field

A slider with its label on top, beside a number field with its label on top, so the two controls start on one line.

Quality80%

Exports at 1600 px wide and 80% quality

Locked layer

A disabled slider: the logo's opacity holds while its layer is locked and moves again once it's unlocked.

Logo opacity60%

Opacity row

An opacity in percent, saved when a drag ends or a key press lands rather than on every frame.

Opacity80%

Saved at 80%

Picture section

Slider rows in an inspector section, whose Reset puts every row back to its default.

Picture

Scale100%
Corners12px

Rotation row

A rotation from -180° to 180° that catches at 0° while dragging and goes back to 0° on a double-click or Backspace.

Rotation0°

Variants and states

Rotation

Drag past 0° and it catches there. Double-click the label or the badge to reset; Shift and an arrow key moves ten degrees.

Rotation-8°

Formats, a filled range and disabled

Scale100%
Blur12px
Depth30

Props

Slider

PropTypeDefaultDescription
label

required

stringWhat the slider sets, shown beside its value and used as its name.
valuenumberThe value. Pair with onValueChange to control it.
defaultValuenumberminThe starting value when uncontrolled. Defaults to min.
onValueChange((value: number) => void)Called on every change, frame by frame while dragging.
onValueCommit((value: number) => void)Called once the user lets go of the thumb or finishes a key press.
minnumber0The lowest value. Defaults to 0.
maxnumber100The highest value. Defaults to 100.
stepnumber1The smallest change. Arrow keys move one step, Shift and an arrow ten. Defaults to 1.
resetValuenumberWhat a double-click on the label or the badge, or Backspace or Delete on the thumb, resets to. Defaults to defaultValue, then min.
detentsreadonly number[]Values a drag snaps to when it passes within 2% of the range, such as 0 for a rotation.
formatSliderFormatHow the badge and screen readers write the value: "%", "°", "px" or a function.
showRangebooleanFills the track up to the thumb. Off by default.
disabledbooleanKeeps the value from changing.
labelPosition"side" | "top""side"Where the label and its value sit: "side" (the default), before a 160px track, as in an inspector row, or "top", on a line above a track that fills the column, as a field's label sits.

It also takes Omit<ComponentPropsWithRef<"div">, "defaultValue" | "onChange" | "dir" | "children">.

Accessibility

Radix's slider: the thumb is role=slider, named by the label, with the formatted value as aria-valuetext. Arrow keys step, Shift and an arrow or Page Up and Page Down step ten, Home and End go to the ends, and Backspace or Delete resets, like a double-click on the label or badge. A press on the thumb focuses it and keeps the value; a press on the track moves the value there and focuses the thumb. Dragging updates the value every frame with no easing.

KeysWhat they do
Arrow Right or Arrow UpMoves the value up one step
Arrow Left or Arrow DownMoves the value down one step
Shift and an arrow, Page Up or Page DownMoves the value ten steps
Home or EndMoves the value to min or max
Backspace or DeleteResets to resetValue, else defaultValue, else min

Motion

It doesn't move.

With reduced motion: Nothing changes.

Layout

It fills its container's width: the label and its value badge at the start, the label truncating with an ellipsis and showing in full as its title, and the track at --bt-w-row-control (160px) at the end. Its height is its 24px thumb's, and the thumb's hit area is 36px square; the badge keeps room for the formatted min and max, so it never changes width.

See it in the Deckhand demo.