HyGradientPicker
Usage examples on this page are written for Lit / plain HTML (
<hy-gradient-picker>). The same component ships asHyGradientPickerin@whitespaceux/harmony-react(native React 19), with typed wrappers for Angular, Solid, Svelte, and Vue. The API reference below applies to all frameworks.
A gradient's stops on one bar: the bar draws the gradient from its start to its end, a handle sits on each stop, and the stops are moved, added and removed on it. It edits where the stops are and how many there are; the color of a stop is chosen beside it, with whatever control fits — hy-color-picker, or a list of a design system's shades — and handed back in stops.
Using it
- Click the bar where there is no handle to add a stop there, on release. It takes the color the gradient already has at that spot, so the bar looks the same, and it is selected, so the control beside it changes the new stop. A press that moves adds nothing.
- Drag a handle to move its stop; a press near a handle grabs it, as a 24 px target would. Past a neighbor the stops re-sort; the moved stop stays selected. Escape while dragging puts the stop back.
- Drag a handle off the bar to remove its stop: it lifts, the bar shows the gradient without it, and release removes it. Back on the bar, it stays. Delete (or Backspace) on a focused handle removes it too. Never below
min-stops. - Keyboard on a handle: the arrows move it 1 % (Shift 10 %), Home and End take it to the ends, Page Up / Page Down 10 %. Each handle is a slider, so a screen reader hears "Stop 2 of 3" and its position (with its color's name when the stop has a
label). caption-selectedwrites the selected stop's name under its handle ("Stop 2"), for a picker whose stops are edited in a list beside it that names them the same way: the name says which row the handle is. It takes room under the bar, so nothing below moves when the selection does. Screen readers already hear the name on the handle.
Its value
stops is the gradient's stop list, { color, position, label? }, positions 0–1. The picker changes its own value, as a slider does, and reports it: input while a handle is dragged (and once more, with the stops put back, when a drag is canceled), change when a move is committed or a stop is added or removed, both with the sorted stops, the reason and the stop's index; select when another stop is selected. A parent that keeps the stops elsewhere writes them back. A stop added between two others takes a hex mixed from them in OKLab, as the bar is drawn; a stop at or past an end takes that end's color and name.
The bar is drawn from the inline start, so it runs right to left in a right-to-left page, and over a checkerboard, so a stop with transparency shows as transparent. It is a straight line whatever the gradient's angle: the angle is set apart (hy-angle-picker).
When to use
- Editing the colors of a gradient and where they sit along it.
Do
- Set
min-stopsto the fewest colors the gradient needs.
When not to use
- Choosing one color:
hy-color-picker. - A value between two ends:
hy-slider, or itsrangemode for a span.
Accessibility: your part
- Name the picker with
label, oraria-label/aria-labelledbyon the host. - Give each stop a
labelwhen its color has a name, so screen readers hear the color, not only a position. - Put the selected stop's color control right after the picker, so Tab reaches it next.
Examples
Basic usage
<hy-gradient-picker label="Banner gradient"></hy-gradient-picker>
<script>
const picker = document.querySelector('hy-gradient-picker');
picker.stops = [
{ color: '#3366ff', position: 0, label: 'Blue' },
{ color: '#ff6699', position: 1, label: 'Pink' },
];
picker.addEventListener('change', (e) => console.log(e.detail.stops));
</script>With a color control for the selected stop
<hy-gradient-picker label="Banner gradient"></hy-gradient-picker>
<hy-color-picker label="Selected color"></hy-color-picker>
<script>
const picker = document.querySelector('hy-gradient-picker');
const color = document.querySelector('hy-color-picker');
picker.stops = [
{ color: '#3366ff', position: 0 },
{ color: '#ff6699', position: 1 },
];
const show = () => (color.value = picker.stops[picker.selected].color);
picker.addEventListener('select', show);
picker.addEventListener('change', show);
color.addEventListener('change', (e) => {
picker.stops = picker.stops.map((s, i) =>
i === picker.selected ? { ...s, color: e.detail.value } : s,
);
});
</script>API
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
stops | — | GradientPickerStop[] | [] | The stops, { color, position, label? }, positions 0–1. Shown sorted by position. |
selected | selected | number | 0 | The index of the selected stop in stops. |
minStops | min-stops | number | 2 | The fewest stops the picker keeps: Delete stops there. |
label | label | string | '' | The picker's visible name. |
helperText | helper-text | string | '' | A short hint below the bar. |
size | size | 'small' | 'medium' | 'large' | 'medium' | Size of the bar, its handles and the row they sit in. |
disabled | disabled | boolean | false | Disables the picker: nothing moves, nothing is added or removed. |
locale | locale | string | undefined | — | BCP-47 locale for the positions read out. Unset, the nearest lang, else the browser's. |
stopLabel | stop-label | string | 'Stop {index} of {count}' | A handle's name: {index} and {count} are the stop's number and how many there are. |
stopValueText | stop-value-text | string | '{name} at {position}' | A named stop's position as read out: {name} is its label, {position} its percentage. |
captionSelected | caption-selected | boolean | false | Writes the selected stop's name under its handle, taking room under the bar for it. |
stopCaptionText | stop-caption-text | string | 'Stop {index}' | The name under the selected handle: {index} and {count} as in stopLabel. |
autofocus | autofocus | boolean | false | Focuses the selected handle on first connect, and marks it for an overlay's discovery. |
Events
| Event | Detail | Description |
|---|---|---|
input | — | Fires while a handle is dragged, and with the stops put back when a drag is canceled. Detail: GradientPickerChangeDetail |
change | — | Fires when a move is committed or a stop is added or removed. Detail: GradientPickerChangeDetail |
select | — | Fires when another stop is selected. Detail: GradientPickerSelectDetail |
CSS Parts
| Part | Description |
|---|---|
base | The outer wrapper (label, bar, helper text) |
label | The label |
bar | The row holding the track and its handles |
track | The bar showing the gradient |
handle | A stop's handle |
caption | The selected stop's name under its handle (caption-selected) |
helper-text | The helper text |
CSS Custom Properties
The documented hooks. Every override hook the component reads — one per token it paints — is listed in @whitespaceux/harmony/theme-api.json, and @whitespaceux/harmony/component-bindings.json (the Harmony MCP get-bindings tool) says which part and state each one paints.
| Property | Default | Description |
|---|---|---|
--hy-gradient-picker-control-height-sm | var(--hy-control-height-sm) | Height of the row the bar sits in, at size small |
--hy-gradient-picker-control-height-md | var(--hy-control-height-md) | Height of the row the bar sits in, at size medium |
--hy-gradient-picker-control-height-lg | var(--hy-control-height-lg) | Height of the row the bar sits in, at size large |
--hy-gradient-picker-track-indicator-size-sm | var(--hy-indicator-size-sm) | Height of the bar, at size small |
--hy-gradient-picker-track-indicator-size-md | var(--hy-indicator-size-md) | Height of the bar, at size medium |
--hy-gradient-picker-track-indicator-size-lg | var(--hy-indicator-size-lg) | Height of the bar, at size large |
--hy-gradient-picker-handle-ornament-pellet | var(--hy-ornament-pellet) | Diameter of a handle, at size small |
--hy-gradient-picker-handle-ornament-knob | var(--hy-ornament-knob) | Diameter of a handle, at size medium |
--hy-gradient-picker-handle-control-height-xs | var(--hy-control-height-xs) | Diameter of a handle, at size large |
--hy-gradient-picker-handle-radius-pill-circle | var(--hy-radius-pill-circle) | Corner of a handle (round) |
--hy-gradient-picker-track-radius-pill-md | var(--hy-radius-pill-md) | Corner of the bar |
--hy-gradient-picker-track-border-default-rest | var(--hy-border-default-rest) | Edge of the bar |
--hy-gradient-picker-handle-background-surface-base | var(--hy-background-surface-base) | Ring around a handle's color |
--hy-gradient-picker-handle-border-default-rest | var(--hy-border-default-rest) | Outer edge of a handle |
--hy-gradient-picker-handle-border-brand-rest | var(--hy-border-brand-rest) | Ring of the selected handle |
--hy-gradient-picker-handle-shadow-elevation-2 | var(--hy-shadow-elevation-2) | Shadow under a handle |
--hy-gradient-picker-foreground-default-rest | var(--hy-foreground-default-rest) | Label color |
--hy-gradient-picker-helper-foreground-subtle-rest | var(--hy-foreground-subtle-rest) | Helper text color |
--hy-gradient-picker-background-neutral-disabled | var(--hy-background-neutral-disabled) | The bar's veil and the handles when disabled |
--hy-gradient-picker-border-default-disabled | var(--hy-border-default-disabled) | Edges when disabled |
--hy-gradient-picker-gap-form-sm | var(--hy-gap-form-sm) | Gap between the label, the bar and the helper text |
--hy-gradient-picker-stroke-sm | var(--hy-stroke-sm) | Width of the bar's edge and a handle's outer edge |
--hy-gradient-picker-stroke-md | var(--hy-stroke-md) | Width of a handle's ring |
--hy-gradient-picker-motion-transition-hover-duration | var(--hy-motion-transition-hover-duration) | Transition duration for hover and selection feedback |
--hy-gradient-picker-motion-transition-hover-easing | var(--hy-motion-transition-hover-easing) | Transition easing for hover and selection feedback |
Methods
focus()
Focuses the selected stop's handle.
blur()
Blurs the focused handle.