Skip to main content

An angle slider is a circular dial that allows users to select an angle, typically in degrees, within a 360° range. It provides an intuitive way to control rotations or orientations, offering accessibility features.

Loading...

Features

  • Fully managed keyboard navigation
  • Supports touch or click on the track to update value
  • Supports right-to-left direction

Installation

Install the angle slider package:

npm install @zag-js/angle-slider @zag-js/vue # or yarn add @zag-js/angle-slider @zag-js/vue

Anatomy

To set up the angle slider correctly, you'll need to understand its anatomy and how we name its parts.

Each part includes a data-part attribute to help identify them in the DOM.

No anatomy available for angle-slider

Usage

Import the angle-slider package:

import * as angleSlider from "@zag-js/angle-slider"

The angle slider package exports two key functions:

  • machine - State machine logic.
  • connect - Maps machine state to JSX props and event handlers.

Pass a unique id to useMachine so generated element ids stay predictable.

Then use the framework integration helpers:

<script setup lang="ts"> import * as angleSlider from "@zag-js/angle-slider" import { normalizeProps, useMachine } from "@zag-js/vue" import { computed } from "vue" const service = useMachine(angleSlider.machine, { id: "1" }) const api = computed(() => angleSlider.connect(service, normalizeProps)) </script> <template> <div v-bind="api.getRootProps()"> <label v-bind="api.getLabelProps()"> Angle Slider: <div v-bind="api.getValueTextProps()">{{ api.valueAsDegree }}</div> </label> <div v-bind="api.getControlProps()"> <div v-bind="api.getThumbProps()"></div> <div v-bind="api.getMarkerGroupProps()"> <div v-for="value in [0, 45, 90, 135, 180, 225, 270, 315]" :key="value" v-bind="api.getMarkerProps({ value })" ></div> </div> </div> <input v-bind="api.getHiddenInputProps()" /> </div> </template>

Setting the initial value

Set defaultValue to define the initial slider value.

const service = useMachine(angleSlider.machine, { defaultValue: 45, })

Controlled angle slider

Use value and onValueChange to control the value externally.

<script setup> import { ref } from "vue" const valueRef = ref(45) const service = useMachine(angleSlider.machine, { get value() { return valueRef.value }, onValueChange(details) { valueRef.value = details.value }, }) </script>

Setting the value's granularity

By default, step is 1, so values move in whole-number increments. Set step to control granularity.

For example, set step to 0.01 for two-decimal precision:

const service = useMachine(angleSlider.machine, { step: 0.01, })

Listening for changes

When the angle slider value changes, the onValueChange and onValueChangeEnd callbacks are invoked.

const service = useMachine(angleSlider.machine, { onValueChange(details) { console.log("value:", details.value) console.log("as degree:", details.valueAsDegree) }, onValueChangeEnd(details) { console.log("final value:", details.value) }, })

Read-only mode

Set readOnly to prevent updates while preserving focus and form semantics.

const service = useMachine(angleSlider.machine, { readOnly: true, })

Usage in forms

To submit the value with a form:

  • Set name on the machine.
  • Render the hidden input from api.getHiddenInputProps().
const service = useMachine(angleSlider.machine, { name: "wind-direction", })

Labeling the thumb for assistive tech

Use aria-label or aria-labelledby when you need custom labeling.

const service = useMachine(angleSlider.machine, { "aria-label": "Wind direction", })

Using angle slider marks

To show marks or ticks along the angle slider track, use the exposed api.getMarkerProps() method to position the angle slider marks at desired angles.

//... <div v-bind="api.getRootProps()"> <label v-bind="api.getLabelProps()"> Angle Slider: <div v-bind="api.getValueTextProps()">{{ api.valueAsDegree }}</div> </label> <div v-bind="api.getControlProps()"> <div v-bind="api.getThumbProps()"></div> <div v-bind="api.getMarkerGroupProps()"> <div v-for="value in [0, 45, 90, 135, 180, 225, 270, 315]" :key="value" v-bind="api.getMarkerProps({ value })" ></div> </div> </div> <input v-bind="api.getHiddenInputProps()" /> </div> //...

Styling guide

Each part includes a data-part attribute you can target in CSS.

Disabled State

When the angle slider is disabled, the data-disabled attribute is added to the root, label, control, thumb and marker.

[data-part="root"][data-disabled] { /* styles for root disabled state */ } [data-part="label"][data-disabled] { /* styles for label disabled state */ } [data-part="control"][data-disabled] { /* styles for control disabled state */ } [data-part="thumb"][data-disabled] { /* styles for thumb disabled state */ } [data-part="range"][data-disabled] { /* styles for thumb disabled state */ }

Invalid State

When the slider is invalid, the data-invalid attribute is added to the root, track, range, label, and thumb parts.

[data-part="root"][data-invalid] { /* styles for root invalid state */ } [data-part="label"][data-invalid] { /* styles for label invalid state */ } [data-part="control"][data-invalid] { /* styles for control invalid state */ } [data-part="valueText"][data-invalid] { /* styles for output invalid state */ } [data-part="thumb"][data-invalid] { /* styles for thumb invalid state */ } [data-part="marker"][data-invalid] { /* styles for marker invalid state */ }

Styling the markers

[data-part="marker"][data-state="(at|under|over)-value"] { /* styles for when the value exceeds the marker's value */ }

Methods and Properties

Machine Context

The angle slider machine exposes the following context properties:

  • idsPartial<{ root: string; thumb: string; hiddenInput: string; control: string; valueText: string; label: string; }>The ids of the elements in the machine. Useful for composition.
  • stepnumberThe step value for the slider.
  • valuenumberThe value of the slider.
  • defaultValuenumberThe initial value of the slider. Use when you don't need to control the value of the slider.
  • onValueChange(details: ValueChangeDetails) => voidThe callback function for when the value changes.
  • onValueChangeEnd(details: ValueChangeDetails) => voidThe callback function for when the value changes ends.
  • disabledbooleanWhether the slider is disabled.
  • readOnlybooleanWhether the slider is read-only.
  • invalidbooleanWhether the slider is invalid.
  • namestringThe name of the slider. Useful for form submission.
  • aria-labelstringThe accessible label for the slider thumb.
  • aria-labelledbystringThe id of the element that labels the slider thumb.
  • dir"ltr" | "rtl"The document's text/writing direction.
  • idstringThe unique identifier of the machine.
  • getRootNode() => ShadowRoot | Node | DocumentA root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.

Machine API

The angle slider api exposes the following methods:

  • valuenumberThe current value of the angle slider
  • valueAsDegreestringThe current value as a degree string
  • setValue(value: number) => voidSets the value of the angle slider
  • draggingbooleanWhether the slider is being dragged.

Data Attributes

Root
data-scope
angle-slider
data-part
root
data-disabled
Present when disabled
data-invalid
Present when invalid
data-readonly
Present when read-only
Label
data-scope
angle-slider
data-part
label
data-disabled
Present when disabled
data-invalid
Present when invalid
data-readonly
Present when read-only
Control
data-scope
angle-slider
data-part
control
data-disabled
Present when disabled
data-invalid
Present when invalid
data-readonly
Present when read-only
Thumb
data-scope
angle-slider
data-part
thumb
data-disabled
Present when disabled
data-invalid
Present when invalid
data-readonly
Present when read-only
Marker
data-scope
angle-slider
data-part
marker
data-value
The value of the item
data-disabled
Present when disabled

CSS Variables

Root
--value
The current value
--angle
The angle in degrees
Marker
--marker-value
The logical marker value (e.g. 0, 45, 90)
--marker-display-value
The rotation angle for display (mirrored in RTL)

Keyboard Interactions

  • ArrowRight
    Increments the angle slider based on defined step
  • ArrowLeft
    Decrements the angle slider based on defined step
  • ArrowUp
    Decreases the value by the step amount.
  • ArrowDown
    Increases the value by the step amount.
  • Shift + ArrowUp
    Decreases the value by a larger step
  • Shift + ArrowDown
    Increases the value by a larger step
  • Home
    Sets the value to 0 degrees.
  • End
    Sets the value to 360 degrees.
Edit this page on GitHub