Skip to content

HyAnglePicker

Usage examples on this page are written for Lit / plain HTML (<hy-angle-picker>). The same component ships as HyAnglePicker in @whitespaceux/harmony-react (native React 19), with typed wrappers for Angular, Solid, Svelte, and Vue. The API reference below applies to all frameworks.

A circular angle picker for inputs that represent a direction — shadow light angle, gradient direction, hue rotation, anything naturally circular. The control reads as a clock face: cardinal labels around the rim, an indicator line pointing from centre to the current angle. Drag the rim, click anywhere inside to snap to that direction, or use arrow keys for fine adjustment.

Form Integration

Implements the Form-Associated Custom Elements API — the current angle (in degrees) is included in FormData under name when the containing form submits. A hidden native <input type="range"> inside the shadow root carries the focus, fires change on commit, and surfaces the value to assistive tech via aria-valuetext.

Why a circle, not a slider

Angles are circular: 0° wraps back to itself; there's no real start or end. A linear slider with 0 on the left and 360 on the right fights that mental model — moving from "Left" forward should land back at "Top", not run off the right edge. Every shipping design tool that takes an angle (Figma, Photoshop, Sketch, After Effects, every CSS colour wheel for hue) uses a circular control for this reason.

Keyboard

The hidden native range input handles keyboard:

  • Arrow keys: ±step (1° default)
  • Shift + arrows: ±45° — snap toward the next cardinal
  • Home / End: 0° / 180°
  • PageUp / PageDown: ±15° (mid-coarse for finer dragging)

Sparse tick marks

Pass tickMarks to label specific compass positions. The picker reuses the slider's TickMark shape so designers learn one vocabulary for "named stops on a numeric range" regardless of the control's geometry. Labels at each angle are auto-positioned on a ring just outside the rim.

Examples

Basic usage

html
<hy-angle-picker label="Light angle" value="45"></hy-angle-picker>

Compass-labelled dial

html
const picker = document.querySelector('hy-angle-picker'); picker.tickMarks = [ { value: 0, label:
'T', ariaLabel: 'Top' }, { value: 90, label: 'R', ariaLabel: 'Right' }, { value: 180, label: 'B',
ariaLabel: 'Bottom' }, { value: 270, label: 'L', ariaLabel: 'Left' }, ];
html
<hy-angle-picker label="Light angle"></hy-angle-picker>

Snap-to-cardinals (no diagonals)

html
picker.tickMarks = [ { value: 0, label: 'T' }, { value: 90, label: 'R' }, { value: 180, label: 'B'
}, { value: 270, label: 'L' }, ];
html
<hy-angle-picker label="Direction" tick-snap></hy-angle-picker>

Form submission

html
<form>
  <hy-angle-picker name="light-angle" value="135"></hy-angle-picker>
  <button type="submit">Save</button>
</form>

API

Properties

PropertyAttributeTypeDefaultDescription
valuevaluenumber0Current angle in compass degrees [0, 360). Values outside this range are wrapped on write.
stepstepnumber1Granularity for keyboard arrow-key adjustments. Default 1° — pointer-driven changes ignore step and operate at pixel precision.
disableddisabledbooleanfalseDisables the picker.
errorerrorbooleanfalseVisual error state.
namenamestring''The name attribute for form submission.
labellabelstring''Primary label displayed above the dial.
hinthintstring''Secondary hint text displayed under the label.
helperTexthelper-textstring''Helper text displayed below the dial.
errorMessageerror-messagestring''Error message shown when error is true (auto-sets error).
sizesize'small' | 'medium' | 'large''medium'Size variant affecting dial diameter.
variantvariant'default' | 'success' | 'warning' | 'danger''default'Visual variant affecting indicator + rim colour.
tickMarksTickMark[][]Sparse named tick marks positioned around the rim. Same shape as hy-slider's tickMarks{ value, label?, ariaLabel? }. Property-only (arrays can't round-trip via HTML attribute); set via Lit .tickMarks=${[...]} or framework property binding. See TickMark (re-exported from this module).
tickSnaptick-snapbooleanfalseWhen true, the picker snaps to the values declared in tickMarks during pointer drag — step still governs keyboard granularity. Defaults to false (tick marks are visual references only). No-op when tickMarks is empty.
inputIdinput-idstring''Explicit ID for the underlying input — auto-generated if empty.
autofocusautofocusbooleanfalseMarks this picker as the auto-focus target. Read by hy-dialog (and other overlay containers) on show to choose initial focus, and applied to the inner native input on first connect.
formHTMLFormElement | null
validityValidityState
validationMessagestring

Events

EventDetailDescription
inputFired during drag / keyboard adjustment. Detail: { value: number }
changeFired when the value is committed (pointer release / blur). Detail: { value: number }

CSS Parts

PartDescription
baseThe outer wrapper (label + dial + helper text column)
labelThe label container (label + hint)
dialThe clickable circle that owns the rim + indicator + labels
inputThe hidden native range input
rimThe visible circle border
indicatorThe line from centre to the current angle
thumbThe thumb dot at the tip of the indicator
hubThe small disc at the centre of the dial
tick-marksContainer for the rim tick lines (only present when tickMarks has entries)
tick-markIndividual tick line on the rim
tick-mark-labelsContainer for tick labels positioned around the rim
tick-mark-labelIndividual label outside the rim
helper-textThe helper text element
error-messageThe error message element

CSS Custom Properties

PropertyDescription
--hy-angle-picker-size-smDial diameter for size=small. Default: 4.5rem
--hy-angle-picker-size-mdDial diameter for size=medium. Default: 6.5rem
--hy-angle-picker-size-lgDial diameter for size=large. Default: 9rem
--hy-angle-picker-rim-colorRim stroke colour
--hy-angle-picker-rim-widthRim stroke width
--hy-angle-picker-fill-colorOptional fill behind the indicator (defaults to transparent)
--hy-angle-picker-indicator-colorColour of the indicator line + hub
--hy-angle-picker-indicator-widthIndicator stroke width
--hy-angle-picker-hub-ornamentDiameter of the centre disc
--hy-angle-picker-thumb-ornamentDiameter of the draggable disc at the indicator tip
--hy-angle-picker-tick-colorColour of rim tick lines (only when tickMarks is set)
--hy-angle-picker-tick-label-colorColour of rim tick labels
--hy-angle-picker-label-radiusRadial distance from centre at which tick labels render. Default: dial radius + 0.75em
--hy-angle-picker-hover-durationTransition duration for hover and focus state feedback
--hy-angle-picker-hover-easingTransition easing for hover and focus state feedback

Built with Lit. Documented with VitePress.