Vize

Knob

Rotary knob/dial: APG slider semantics with angle-mapped pointer dragging.

Package @vizejs/ui/knob
Maturity stable
Own the source vize lib pull knob
Requires controllable-state, id
Aliases knob, dial, rotary input, potentiometer
Covers WAI-ARIA Slider, React Aria useSlider (rotary), Web Audio UI knobs

Usage

import { Knob } from "@vizejs/ui/knob";

Or copy the source into your project with vize lib pull knob (see Source Distribution).

API

Knob

Source: src/families/form/knob/knob.vue

Props

Prop Type Default Description
id string | null undefined Element id. null and undefined select a deterministic fallback.
name string undefined Native form field name; a hidden input submits the value.
modelValue number undefined Controlled value. undefined selects uncontrolled behavior.
defaultValue number min Initial uncontrolled value, also restored by form reset.
min number 0 Lower bound.
max number 100 Upper bound (repaired to min + 1 when not greater than min).
step number 1 Positive step.
largeStep number step * 10 Positive step for Page Up and Page Down.
startAngle number -135 Indicator angle of min, in degrees clockwise from 12 o'clock.
endAngle number 135 Indicator angle of max; the sweep must be positive and at most 360°.
allowWheel boolean false Opt in to wheel stepping while focused.
disabled boolean false Disable interaction and form submission.
readOnly boolean false Keep focus and form value while preventing user changes.
getValueText (value: number) => string undefined Formats aria-valuetext, for example (v) => \${v} dB``.
ariaLabel string undefined Accessible name when no label or aria-labelledby supplies one.
ariaLabelledby string undefined Ids that label the knob.
ariaDescribedby string undefined Ids that describe the knob.
ariaErrormessage string undefined Id of the validation error message used while invalid.
ariaInvalid KnobAriaInvalid false Invalid state announced to assistive technology.

Events

Event Payload Description
update:modelValue [value: number] Fired for every requested value, including drag frames.
change [value: number, source: KnobChangeSource] Fired once a change is committed (key press, pointer release, wheel, or API).

Slots

Slot Slot props Description
default KnobSlotState Renders the dial face and indicator with the angle and value.

Exposed

Member Type Description
value number Current value.
angle number Indicator angle in degrees (clockwise from 12 o'clock).
percent number Position within the bounds, 0–100.
min number Normalized lower bound.
max number Normalized upper bound.
dragging boolean Whether a pointer drag is active.
disabled boolean Whether interaction is disabled.
state KnobState Stable state token.
element HTMLSpanElement | null Rendered role="slider" element.
focus (options?: FocusOptions) => void Focus the rotary element.
setValue (value: number) => boolean Request a value (snapped/clamped or wrapped); returns whether it changed.
reset () => boolean Restore the default value; returns whether it changed.

Behavior

Normative state x input -> outcome table for knob.vue (@vizejs/ui/knob), a rotary control with WAI-ARIA APG slider semantics. Angles are degrees clockwise from 12 o'clock over a sweep (default -135° to 135°). Every row is proven by the named test in knob.test.ts or knob-ssr.test.ts; compile-only assertions live in knob.types.test-d.ts.

# State Input Outcome Proven by
K1 geometry angle <-> value values map linearly onto the sweep; pointer angles in the dead zone resolve to the nearer end (no jump across ends) maps angles and values with a dead zone that never jumps across ends
K2 named render focusable role="slider" with aria-valuenow/min/max/valuetext, --vize-knob-angle/--vize-knob-percent, hidden value renders an APG slider with angle hooks and a hidden form value
K3 focused Arrow / Page / Home / End Up/Right increase, Down/Left decrease, pages use largeStep, Home/End jump to bounds; each distinct change commits keyboard steps, pages, and jumps to bounds, committing each change
K4 idle pointer drag press focuses and maps the pointer angle around the center; moves update the value; release commits once dragging rotates around the center and commits once on release
K5 wheel / locked wheel, keys, pointer wheel is opt-in and needs focus; disabled and read-only knobs ignore input and emit nothing wheel is opt-in and needs focus; disabled and read-only knobs ignore input
K6 controlled / form keys, reset, API controlled values win; the hidden input submits and form reset restores defaultValue; setValue snaps; reset, focus controlled values, form reset, and the imperative API
K7 SSR / hydration isolated requests byte-identical markup with the CSS angle and no NaN/Infinity; hydration without diagnostics renders byte-identical rotary markup and hydrates without mismatches

Public extension contract

Surface Contract
Parts root (the role="slider" element; the slot draws the face).
Data attributes data-vize-ui="knob", data-state (idle/dragging/readonly/disabled).
CSS custom properties --vize-knob-angle (deg) and --vize-knob-percent.

The subpath is tree-shakable and ships no CSS; those package contracts are pinned by distribution.test.ts, check:size, and check:tree-shaking.