Knob

<o-knob> is a rotary control you turn with the pointer, the wheel or the keyboard.

Grab the band and turn it, or drag up and down anywhere else on the knob. The track is an interactive <o-arc> with a hit-width, so it takes the pointer along its whole length, a few pixels past its edges too.

Attributes

AttributeDefaultWhat it does
valueminStarting value, snapped to step and kept in range. It is also the default a form reset returns to; the attribute follows the knob as it turns.
min / max0 / 100The range. A max below min becomes min.
step1Increment for keys and the wheel, and the snapping grid. Decimals work; any turns snapping off.
range / from270 / autoArc span and start angle.
labelText in the middle instead of the value. It is also the default accessible name.
unitSuffix for the value. With format="unit", an Intl unit.
fill / track#a1a1aa / #27272aValue and track colors.
thickness2.4Band thickness in lanes.
shaperoundedEnding of the value arc.
nameField name: with it, the knob submits its value with the form. See Forms.
disabledoffDimmed, out of the tab order and not submitted. A disabled <fieldset> does the same. Setting value from code still works.
size160pxWidth of the widget.
glowoffA glow in the value color.
format, decimals, currency, localeNumber format of the readout and aria-valuetext, through Intl.NumberFormat. See Shared options.
themedark, light or glass. Also inherited from an ok-theme-* class on a container. See Shared options.

Properties and events

NameWhat it does
valueReads or sets the value. Setting it snaps and clamps like the attribute and fires no event, like a native input.
min, max, step, stepUp(), stepDown()As on <input type="range">.
form, labels, defaultValue, validityAs on a native input.
formatterA function that writes the readout and aria-valuetext. See Shared options.
inputFires on every change you make: turning, keys, the wheel. event.detail.value has the value.
changeFires once a gesture ends with a different value, and on each key press or wheel step.
knob.addEventListener('input', (e) => preview(e.detail.value))
knob.addEventListener('change', (e) => save(e.detail.value))
knob.value = 40

The knob is a form control. Inside a <form> with a name, it submits its value, resets with the form and comes back on page restore:

<form>
  <o-knob name="volume" value="30"></o-knob>
</form>

Styling

PropertyDefaultWhat it does
--ok-fill#a1a1aaValue arc.
--ok-track#27272aTrack.
--ok-focus#3da9fcFocus ring.
--ok-hub-colorinheritsColor of the readout in the middle.
--ok-hub-bgnoneBackground of the hole in the middle.
--ok-hub-size1.4emFont size of the readout.
--ok-font-size8cqminBase font size, as a share of the widget. Text scales with it.

More examples

A row of knobs with readouts

Decimals and a full turn

Disabled

Accessibility

The knob is a role="slider" with aria-valuenow, aria-valuemin, aria-valuemax, aria-valuetext and aria-disabled. It is one tab stop. Arrow keys move one step, Page Up and Page Down ten, Home and End go to the ends. The wheel only turns it while it has focus, so scrolling the page never changes a value by accident.

Without a name it is called “Rotary control”, or takes its label. Give each knob an aria-label or aria-labelledby.