Orbit Dial
A circular control with radial ticks, a numeric readout, and a native range slider.
Demo
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.jsonCode
"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
| Prop | Type | Default | Purpose |
|---|---|---|---|
label | string | Required | Text inside the dial and the slider's accessible name. |
ariaLabel | string | label | A longer accessible name when the visible label is abbreviated. |
value | number | None | Controlled value. Pair with onValueChange. |
defaultValue | number | min | Initial value in uncontrolled mode. |
onValueChange | (value: number) => void | None | Runs when an interaction changes the value. |
min | number | 0 | Lowest allowed value and the starting point for steps. |
max | number | 100 | Upper limit. If it falls between steps, the last complete step is used. |
step | number | 1 | Positive increment, including decimals such as 0.1. |
disabled | boolean | false | Disables both the dial and slider. |
unit | string | None | Suffix beside the readout, such as % or dB. |
formatValue | (value: number) => string | String | Formats the numeric readout and boundary labels. Keep it short enough to fit. |
getValueText | (value: number) => string | Formatted value and unit | Spoken value for assistive technology, such as 64 percent. |
id | string | Generated | ID of the native range input. |
name | string | None | Includes the current value in form submission. |
className | string | None | Outer layout classes. The dial fills its container up to 16rem wide. |
style | CSSProperties | None | Inline 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.