Skip to content

HyAnglePicker

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>

Events

  • input - Fired during drag / keyboard adjustment. Detail: { value: number }
  • change - Fired when the value is committed (pointer release / blur). Detail: { value: number }

Built with Lit. Documented with VitePress.