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
<hy-angle-picker label="Light angle" value="45"></hy-angle-picker>Compass-labelled dial
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' }, ];<hy-angle-picker label="Light angle"></hy-angle-picker>Snap-to-cardinals (no diagonals)
picker.tickMarks = [ { value: 0, label: 'T' }, { value: 90, label: 'R' }, { value: 180, label: 'B'
}, { value: 270, label: 'L' }, ];<hy-angle-picker label="Direction" tick-snap></hy-angle-picker>Form submission
<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 }