a1ui
6 new components shipped

Orbit Dial

A circular control with radial ticks, a numeric readout, and a native range slider.

Read agent guide

Demo

Texture study01 / Output
Output level64%
SubtlePronounced

Turn the dial or use the slider below it. Arrow keys change one step; Home and End jump to the limits.

Installation

pnpm dlx shadcn@latest add https://ui.arjayby1.xyz/r/orbit-dial.json

Code

"use client";

import { useState } from "react";

import { OrbitDial } from "@/components/ui/orbit-dial";

export function OrbitDialExample() {
  const [intensity, setIntensity] = useState(64);

  return (
    <OrbitDial
      label="Intensity"
      value={intensity}
      onValueChange={setIntensity}
      unit="%"
      getValueText={(value) => `${value} percent`}
    />
  );
}

API reference

PropTypeDefaultPurpose
labelstringRequiredText inside the dial and the slider's accessible name.
ariaLabelstringlabelA longer accessible name when the visible label is abbreviated.
valuenumberNoneControlled value. Pair with onValueChange.
defaultValuenumberminInitial value in uncontrolled mode.
onValueChange(value: number) => voidNoneRuns when an interaction changes the value.
minnumber0Lowest allowed value and the starting point for steps.
maxnumber100Upper limit. If it falls between steps, the last complete step is used.
stepnumber1Positive increment, including decimals such as 0.1.
disabledbooleanfalseDisables both the dial and slider.
unitstringNoneSuffix beside the readout, such as % or dB.
formatValue(value: number) => stringStringFormats the numeric readout and boundary labels. Keep it short enough to fit.
getValueText(value: number) => stringFormatted value and unitSpoken value for assistive technology, such as 64 percent.
idstringGeneratedID of the native range input.
namestringNoneIncludes the current value in form submission.
classNamestringNoneOuter layout classes. The dial fills its container up to 16rem wide.
styleCSSPropertiesNoneInline styles and component CSS variables.

Drag the circular face with a mouse, pen, or touch. The dial stops at either end of its 270-degree arc, including when dragging through the gap at the bottom. The center ignores pointer movement to avoid sudden angle changes.

The slider below the dial provides a straight-line alternative. Focus it and use arrow keys to move one step, or Home and End to jump to the limits. The native input supplies the control's accessible name, value, disabled state, and form value. Clicking the dial also focuses this input.

Values are clamped to the range and rounded to steps relative to min. For example, min={0.1}, max={1}, and step={0.2} allow 0.1, 0.3, 0.5, 0.7, and 0.9. Equal or reversed limits disable the control at the minimum. Non-finite bounds fall back to 0 and 100, and a non-positive or non-finite step falls back to 1. Ranges whose span or step count cannot be represented safely are disabled.

The component uses the project's --background, --foreground, --muted-foreground, and --border colors with a monospace font. Override --orbit-dial-background, --orbit-dial-foreground, --orbit-dial-muted, --orbit-dial-border, or --orbit-dial-face through style for a local variation. Values update directly without automatic motion or transition delays.