# Rotary dial
> Rotary dial that follows the pointer around a 270° arc, flashing a tick and rolling the value at each detent, then springs onto the step.
- React: `import { UiDial } from "@/components/lumesec/ui-dial"`
- Collection: Interaction (https://elements.lumesec.ai/components/interaction)
- Registry item: https://elements.lumesec.ai/r/ui-dial.json
- Page: https://elements.lumesec.ai/components/interaction/dial



Live preview: https://elements.lumesec.ai/view/ui-dial

Demo source:

```tsx
import { UiDial } from "@/components/lumesec/ui-dial";

export default function UiDialDemo() {
  return (
    <div className="grid w-full max-w-[380px] justify-items-center">
      <UiDial defaultValue={0.7} min={0} max={1} step={0.1} label="Temperature" />
    </div>
  );
}
```

## Playground

Change a prop and the component re-renders. Props marked remounts set an initial value, so the component starts over.

## Installation

```bash
npx shadcn@latest add @lumesec/ui-dial
```

First time with the @lumesec registry? Register it once, or install by URL:

```bash
npx shadcn@latest registry add @lumesec=https://elements.lumesec.ai/r/{name}.json
npx shadcn@latest add https://elements.lumesec.ai/r/ui-dial.json
```

## Usage

React:

```tsx
import { UiDial } from "@/components/lumesec/ui-dial";

export function Example() {
  return <UiDial defaultValue={0.7} min={0} max={1} step={0.1} label="Temperature" />;
}
```

## Behaviour

Drag around the knob and it follows your pointer, with a click at each detent: the tick flashes, the knob nudges and the value rolls. Let go and it springs onto the nearest step.

## API reference

### Props

Also accepts every prop of `<div>` (`React.ComponentProps<"div">`), spread onto the root element.

| Prop            | Type                      | Default         | Description                                                                                   |
| --------------- | ------------------------- | --------------- | --------------------------------------------------------------------------------------------- |
| `value`         | `number`                  | —               | Controlled value, snapped to `step` and clamped to `min` and `max`. Use with `onValueChange`. |
| `defaultValue`  | `number`                  | `0.7`           | Initial value when uncontrolled.                                                              |
| `min`           | `number`                  | `0`             | Lowest value.                                                                                 |
| `max`           | `number`                  | `1`             | Highest value.                                                                                |
| `step`          | `number`                  | `0.1`           | Detent size. Its decimal places set the display precision.                                    |
| `label`         | `string`                  | `"Temperature"` | Caption under the dial and the slider's `aria-label`.                                         |
| `onValueChange` | `(value: number) => void` | —               | Called on every detent change from drag, wheel or keys.                                       |
| `onValueCommit` | `(value: number) => void` | —               | Called with each wheel or key step, and when a drag ends, even if the value did not change.   |

### Ref

`ref` points at the root `HTMLDivElement`.

## Keyboard

| Keys                  | Action                 |
| --------------------- | ---------------------- |
| ArrowUp / ArrowRight  | Increase by one step.  |
| ArrowDown / ArrowLeft | Decrease by one step.  |
| Home / End            | Jump to `min` / `max`. |

## Accessibility

* The dial has `role="slider"`, `tabindex="0"`, `aria-valuenow`, `aria-valuemin`, `aria-valuemax` and an `aria-label` from `label`.
* Pressing on the dial moves focus to it.
* The tick marks are `aria-hidden`, as are the min and max labels.
* Reduced motion: the knob settles on the step angle without overshoot, ticks do not flash, the knob does not nudge and the value swaps without rolling.

## Theming

Styled with Tailwind classes on your shadcn theme tokens, so light and dark follow your theme. The accent comes from `--lumesec`. See [Theming](/docs/theming).

This component reads `--lumesec`, `--lumesec-glint`, `--foreground`, `--muted-foreground`, `--card`, `--muted` and `--border`.

## Notes

* Dragging into the 90° gap at the bottom of the arc sticks to the nearer end. The scroll wheel over the dial changes one step per tick and does not scroll the page; sideways scrolling is ignored.
* With 16 or fewer detents each step gets a major tick plus two minor ticks; with more, one tick per detent.
* Changing `value` from outside springs the knob to it and flashes its tick without calling either callback.


