Fader

faderControls

A recessed groove, a lit fill, and a grippy cap. Pressing anywhere on the track jumps the cap to that spot and keeps dragging from there — absolute positioning, the way a real mixer behaves — while Shift switches to a relative fine pass without yanking the cap under the cursor. Optional tick marks take either a count or explicit values, so a dB scale lands where the numbers actually are. Six variants re-skin it from a moulded cap to a console blade, a cream vintage face or a capless hairline.

default
flat
channel
vintage
minimal
neon
Out
0.0 dB
Send
25%
Mix
50%

Install

Copies the source into your project. Anything it depends on comes with it.

$ npx shadcn add @syntho-ui/fader

Pulls in

fader-headlessuse-surface-pointeruse-wheeluse-first-interactionknob-corecontrol-skincontrol-labelcontrol-mathcontrol-formatcontrol-themeuse-control

First time? Add the registry to your components.json once:

components.json
{
  "registries": {
    "@syntho-ui": "https://engine.syntho.app/r/{name}.json"
  }
}

Usage

Controlled or uncontrolled, whichever suits the surrounding state.

example.tsx
import { Fader } from "@/components/ui/fader"
import { decibels } from "@/lib/format"

<Fader
  label="Out"
  min={-60}
  max={6}
  defaultValue={0}
  format={decibels()}
  ticks={[6, 0, -6, -12, -24, -60]}
/>

Props

PropTypeDefaultDescription
valuenumberControlled value. Omit to let the control own its state.
defaultValuenumber0Starting value when uncontrolled, and the double-click reset target.
onChange(value: number) => voidFires on every value change, in both controlled and uncontrolled modes.
onChangeEnd(value: number) => voidFires once when a drag gesture finishes — the hook for undo coalescing.
minnumber0Lower bound of the range.
maxnumber1Upper bound of the range.
stepnumber0Quantisation grid. 0 means continuous.
labelstringCaption above the control. Doubles as the accessible name.
unitstringSuffix appended by the default formatter, e.g. "Hz".
format(value: number) => stringCustom readout formatter. Overrides `unit`.
hideValuebooleanfalseHide the numeric readout.
disabledbooleanfalseBlock interaction and dim the control.
variant"default" | "flat" | "channel" | "vintage" | "minimal" | "neon""default"Visual preset. flat is a matte cap over a solid fill, channel is the long thin blade of a console strip, vintage a cream knurled cap, minimal drops the cap for a hairline at the value, and neon keeps the fill glowing at rest. Looks only — every variant behaves identically. An explicit variant wins over an inherited skin.
skin"default" | "flat" | "vintage" | "minimal" | "neon"Finish, shared with every other control in the kit. Omitted, the fader takes whatever a surrounding ControlSkinProvider declares.
orientation"vertical" | "horizontal""vertical"Which way the fader travels.
lengthnumber140Long axis in pixels.
thicknessnumber34Short axis in pixels — the width of the cap.
ticksnumber | number[]A count for evenly spaced ticks, or explicit values in the control's own units.
wheelSensitivitynumber1/70 ≈ 0.0143Normalised units per wheel notch. The default crosses a continuous range in about 70 notches and guarantees one declared step per notch for stepped ranges; 0 disables the wheel.
onInteraction(event) => voidFires once, on the first gesture, synchronously inside the event — for resuming a suspended AudioContext.

Interaction

  • Drag with pointer capture, so the gesture keeps tracking past the edge of the control.
  • Shift while dragging for a fine pass; release it mid-gesture and coarse tracking resumes.
  • Double-click resets to defaultValue.
  • Arrow keys step normally, Shift+arrow steps finely, PageUp/PageDown jump by ten, Home/End go to the extremes.
  • Proper slider and switch roles with live aria-valuetext, so a screen reader announces the formatted value rather than the raw number.
  • The scroll wheel adjusts the value while the pointer is over the control; stepped ranges move at least one declared step per notch by default.