{"slug":"angle-slider","title":"Angle Slider","description":"Using the angle slider machine in your project.","contentType":"component","framework":"react","content":"An angle slider is a circular dial that allows users to select an angle,\ntypically in degrees, within a 360° range. It provides an intuitive way to\ncontrol rotations or orientations, offering accessibility features.\n\n## Resources\n\n\n[Latest version: v1.35.3](https://www.npmjs.com/package/@zag-js/angle-slider)\n[Logic Visualizer](https://zag-visualizer.vercel.app/angle-slider)\n[Source Code](https://github.com/chakra-ui/zag/tree/main/packages/machines/angle-slider)\n\n\n\n**Features**\n\n- Fully managed keyboard navigation\n- Supports touch or click on the track to update value\n- Supports right-to-left direction\n\n## Installation\n\nInstall the angle slider package:\n\n```bash\nnpm install @zag-js/angle-slider @zag-js/react\n# or\nyarn add @zag-js/angle-slider @zag-js/react\n```\n\n## Anatomy\n\nTo set up the angle slider correctly, you'll need to understand its anatomy and\nhow we name its parts.\n\n> Each part includes a `data-part` attribute to help identify them in the DOM.\n\n\n\n## Usage\n\nImport the angle-slider package:\n\n```jsx\nimport * as angleSlider from \"@zag-js/angle-slider\"\n```\n\nThe angle slider package exports two key functions:\n\n- `machine` - State machine logic.\n- `connect` - Maps machine state to JSX props and event handlers.\n\n> Pass a unique `id` to `useMachine` so generated element ids stay predictable.\n\nThen use the framework integration helpers:\n\n```jsx\nimport * as angleSlider from \"@zag-js/angle-slider\"\nimport { normalizeProps, useMachine } from \"@zag-js/react\"\n\nexport function AngleSlider() {\n  const service = useMachine(angleSlider.machine, { id: \"1\" })\n\n  const api = angleSlider.connect(service, normalizeProps)\n\n  return (\n    <div {...api.getRootProps()}>\n      <label {...api.getLabelProps()}>Wind direction</label>\n      <div {...api.getControlProps()}>\n        <div {...api.getThumbProps()}></div>\n        <div {...api.getMarkerGroupProps()}>\n          {[0, 45, 90, 135, 180, 225, 270, 315].map((value) => (\n            <div key={value} {...api.getMarkerProps({ value })}></div>\n          ))}\n        </div>\n      </div>\n      <div {...api.getValueTextProps()}>{api.value} degrees</div>\n      <input {...api.getHiddenInputProps()} />\n    </div>\n  )\n}\n```\n\n### Setting the initial value\n\nSet `defaultValue` to define the initial slider value.\n\n```jsx {2}\nconst service = useMachine(angleSlider.machine, {\n  defaultValue: 45,\n})\n```\n\n### Controlled angle slider\n\nUse `value` and `onValueChange` to control the value externally.\n\n```tsx\nimport { useState } from \"react\"\n\nexport function ControlledAngleSlider() {\n  const [value, setValue] = useState(45)\n\n  const service = useMachine(angleSlider.machine, {\n    value,\n    onValueChange(details) {\n      setValue(details.value)\n    },\n  })\n\n  return (\n    // ...\n  )\n}\n```\n\n### Setting the value's granularity\n\nBy default, `step` is `1`, so values move in whole-number increments. Set `step`\nto control granularity.\n\nFor example, set `step` to `0.01` for two-decimal precision:\n\n```jsx {2}\nconst service = useMachine(angleSlider.machine, {\n  step: 0.01,\n})\n```\n\n### Listening for changes\n\nWhen the angle slider value changes, the `onValueChange` and `onValueChangeEnd`\ncallbacks are invoked.\n\n```jsx {2-7}\nconst service = useMachine(angleSlider.machine, {\n  onValueChange(details) {\n    console.log(\"value:\", details.value)\n    console.log(\"as degree:\", details.valueAsDegree)\n  },\n  onValueChangeEnd(details) {\n    console.log(\"final value:\", details.value)\n  },\n})\n```\n\n### Read-only mode\n\nSet `readOnly` to prevent updates while preserving focus and form semantics.\n\n```jsx {2}\nconst service = useMachine(angleSlider.machine, {\n  readOnly: true,\n})\n```\n\n### Usage in forms\n\nTo submit the value with a form:\n\n- Set `name` on the machine.\n- Render the hidden input from `api.getHiddenInputProps()`.\n\n```jsx {2}\nconst service = useMachine(angleSlider.machine, {\n  name: \"wind-direction\",\n})\n```\n\n### Labeling the thumb for assistive tech\n\nUse `aria-label` or `aria-labelledby` when you need custom labeling.\n\n```jsx {2}\nconst service = useMachine(angleSlider.machine, {\n  \"aria-label\": \"Wind direction\",\n})\n```\n\n### Using angle slider marks\n\nTo show marks or ticks along the angle slider track, use the exposed\n`api.getMarkerProps()` method to position the angle slider marks at desired\nangles.\n\n```jsx {7-11}\n//...\n\n<div {...api.getRootProps()}>\n  <label {...api.getLabelProps()}>Wind direction</label>\n  <div {...api.getControlProps()}>\n    <div {...api.getThumbProps()}></div>\n    <div {...api.getMarkerGroupProps()}>\n      {[0, 45, 90, 135, 180, 225, 270, 315].map((value) => (\n        <div key={value} {...api.getMarkerProps({ value })}></div>\n      ))}\n    </div>\n  </div>\n  <div {...api.getValueTextProps()}>{api.value} degrees</div>\n  <input {...api.getHiddenInputProps()} />\n</div>\n//...\n```\n\n## Styling guide\n\nEach part includes a `data-part` attribute you can target in CSS.\n\n### Disabled State\n\nWhen the angle slider is disabled, the `data-disabled` attribute is added to the\nroot, label, control, thumb and marker.\n\n```css\n[data-part=\"root\"][data-disabled] {\n  /* styles for root disabled state */\n}\n\n[data-part=\"label\"][data-disabled] {\n  /* styles for label disabled state */\n}\n\n[data-part=\"control\"][data-disabled] {\n  /* styles for control disabled state */\n}\n\n[data-part=\"thumb\"][data-disabled] {\n  /* styles for thumb disabled state */\n}\n\n[data-part=\"range\"][data-disabled] {\n  /* styles for thumb disabled state */\n}\n```\n\n### Invalid State\n\nWhen the slider is invalid, the `data-invalid` attribute is added to the root,\ntrack, range, label, and thumb parts.\n\n```css\n[data-part=\"root\"][data-invalid] {\n  /* styles for root invalid state */\n}\n\n[data-part=\"label\"][data-invalid] {\n  /* styles for label invalid state */\n}\n\n[data-part=\"control\"][data-invalid] {\n  /* styles for control invalid state */\n}\n\n[data-part=\"valueText\"][data-invalid] {\n  /* styles for output invalid state */\n}\n\n[data-part=\"thumb\"][data-invalid] {\n  /* styles for thumb invalid state */\n}\n\n[data-part=\"marker\"][data-invalid] {\n  /* styles for marker invalid state */\n}\n```\n\n### Styling the markers\n\n```css\n[data-part=\"marker\"][data-state=\"(at|under|over)-value\"] {\n  /* styles for when the value exceeds the marker's value */\n}\n```\n\n## Methods and Properties\n\n### Machine Context\n\nThe angle slider machine exposes the following context properties:\n\n**`ids`**\nType: `Partial<{ root: string; thumb: string; hiddenInput: string; control: string; valueText: string; label: string; }>`\nDescription: The ids of the elements in the machine.\nUseful for composition.\n\n**`step`**\nType: `number`\nDescription: The step value for the slider.\n\n**`value`**\nType: `number`\nDescription: The value of the slider.\n\n**`defaultValue`**\nType: `number`\nDescription: The initial value of the slider.\nUse when you don't need to control the value of the slider.\n\n**`onValueChange`**\nType: `(details: ValueChangeDetails) => void`\nDescription: The callback function for when the value changes.\n\n**`onValueChangeEnd`**\nType: `(details: ValueChangeDetails) => void`\nDescription: The callback function for when the value changes ends.\n\n**`disabled`**\nType: `boolean`\nDescription: Whether the slider is disabled.\n\n**`readOnly`**\nType: `boolean`\nDescription: Whether the slider is read-only.\n\n**`invalid`**\nType: `boolean`\nDescription: Whether the slider is invalid.\n\n**`name`**\nType: `string`\nDescription: The name of the slider. Useful for form submission.\n\n**`aria-label`**\nType: `string`\nDescription: The accessible label for the slider thumb.\n\n**`aria-labelledby`**\nType: `string`\nDescription: The id of the element that labels the slider thumb.\n\n**`dir`**\nType: `\"ltr\" | \"rtl\"`\nDescription: The document's text/writing direction.\n\n**`id`**\nType: `string`\nDescription: The unique identifier of the machine.\n\n**`getRootNode`**\nType: `() => ShadowRoot | Node | Document`\nDescription: A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.\n\n### Machine API\n\nThe angle slider `api` exposes the following methods:\n\n**`value`**\nType: `number`\nDescription: The current value of the angle slider\n\n**`valueAsDegree`**\nType: `string`\nDescription: The current value as a degree string\n\n**`setValue`**\nType: `(value: number) => void`\nDescription: Sets the value of the angle slider\n\n**`dragging`**\nType: `boolean`\nDescription: Whether the slider is being dragged.\n\n### Data Attributes\n\n**`Root`**\n\n**`data-scope`**: angle-slider\n**`data-part`**: root\n**`data-disabled`**: Present when disabled\n**`data-invalid`**: Present when invalid\n**`data-readonly`**: Present when read-only\n\n**`Label`**\n\n**`data-scope`**: angle-slider\n**`data-part`**: label\n**`data-disabled`**: Present when disabled\n**`data-invalid`**: Present when invalid\n**`data-readonly`**: Present when read-only\n\n**`Control`**\n\n**`data-scope`**: angle-slider\n**`data-part`**: control\n**`data-disabled`**: Present when disabled\n**`data-invalid`**: Present when invalid\n**`data-readonly`**: Present when read-only\n\n**`Thumb`**\n\n**`data-scope`**: angle-slider\n**`data-part`**: thumb\n**`data-disabled`**: Present when disabled\n**`data-invalid`**: Present when invalid\n**`data-readonly`**: Present when read-only\n\n**`Marker`**\n\n**`data-scope`**: angle-slider\n**`data-part`**: marker\n**`data-value`**: The value of the item\n**`data-state`**: \n**`data-disabled`**: Present when disabled\n\n### CSS Variables\n\n<CssVarTable name=\"angle-slider\" />\n\n### Keyboard Interactions\n\n**`ArrowRight`**\nDescription: <span>Increments the angle slider based on defined step</span>\n\n**`ArrowLeft`**\nDescription: <span>Decrements the angle slider based on defined step</span>\n\n**`ArrowUp`**\nDescription: <span>Decreases the value by the step amount.</span>\n\n**`ArrowDown`**\nDescription: <span>Increases the value by the step amount.</span>\n\n**`Shift + ArrowUp`**\nDescription: <span>Decreases the value by a larger step</span>\n\n**`Shift + ArrowDown`**\nDescription: <span>Increases the value by a larger step</span>\n\n**`Home`**\nDescription: Sets the value to 0 degrees.\n\n**`End`**\nDescription: Sets the value to 360 degrees.","package":"@zag-js/angle-slider","editUrl":"https://github.com/chakra-ui/zag/edit/main/website/data/components/angle-slider.mdx"}