<hue-wheel>
A polar color wheel: hue around the circle, and (optionally) a second channel along the radius. Works in any polar color space: nothing is hardcoded, everything is derived from the space’s metadata.
Basic use (just hue, rendered as a ring):
<hue-wheel space="oklch"></hue-wheel>
Add a radial axis with channel (here, chroma) and a starting color:
<hue-wheel space="oklch" channel="c" color="oklch(0.7 0.15 200)"></hue-wheel>
Any polar space works:
<hue-wheel space="hsl" channel="s" color="hsl(200 80% 50%)"></hue-wheel>
Click or drag to set the color (or to add one if none is present); once the marker is focused, arrow keys nudge it (←→ hue, ↑↓ channel). Use readonly to display without editing:
<hue-wheel space="lch" channel="c" color="lch(60 50 120)" readonly></hue-wheel>
Extra points
Slot <color-swatch> or <color-scale> elements to plot additional (non-editable) points, positioned by their own hue and channel. Hovering a point reveals its details as a tooltip.
<hue-wheel space="oklch" channel="c" color="oklch(0.7 0.15 200)">
<color-scale space="oklch"
colors="oklch(0.6 0.2 20), oklch(0.7 0.25 140), oklch(0.5 0.18 300)"></color-scale>
<color-swatch>oklch(0.8 0.1 90)</color-swatch>
</hue-wheel>
Custom marker
By default the current color is shown as a plain dot with no tooltip. To customize it, slot your own <color-swatch slot="marker"> — hue-wheel keeps its color in sync, positions it, and makes it the slider, while you control its looks (and can re-enable a details tooltip via info + --details-style: compact):
<hue-wheel space="oklch" channel="c" color="oklch(0.7 0.15 200)">
<color-swatch slot="marker" info="oklch.h, oklch.c"
style="--dot-size: 1.4em; --dot-border: 3px solid black; --details-style: compact"></color-swatch>
</hue-wheel>
Building a basic custom color picker
A <hue-wheel> covers two of a color’s three coordinates: hue (the angle) and one radial channel (here, chroma). Pair it with a <channel-slider> for the remaining channel (lightness) and a <space-picker> to switch polar spaces, and you have a full color picker. A <color-swatch> shows the result.
The wheel owns hue + chroma, the slider owns lightness, and the space picker switches the whole thing into another polar space (we list the polar spaces that have a lightness channel, since the slider is fixed to l). The wheel’s radial channel is whatever’s left over — the non-hue, non-lightness coordinate, so it’s chroma in oklch/lch and saturation in hsl/okhsl. Each control mirrors its color to the others (and the swatch) on input:
<div class="wheel-picker">
<label>
Space:
<space-picker id="cp_space" spaces="oklch, okhsl, lch, hsl" value="oklch"
oninput="let s = this.selectedSpace; cp_wheel.space = cp_lightness.space = s; cp_wheel.channel = Object.keys(s.coords).find(c => c !== 'l' && s.coords[c].type !== 'angle')"></space-picker>
</label>
<hue-wheel id="cp_wheel" space="oklch" channel="c" color="oklch(0.7 0.18 200)"
oninput="cp_lightness.color = cp_out.color = this.color"></hue-wheel>
<channel-slider id="cp_lightness" space="oklch" channel="l" color="oklch(0.7 0.18 200)"
oninput="cp_wheel.color = cp_out.color = this.color">Lightness</channel-slider>
<color-swatch id="cp_out" size="large">oklch(0.7 0.18 200)</color-swatch>
</div>
<style>
.wheel-picker {
display: flex;
flex-flow: column;
align-items: center;
gap: 1em;
channel-slider {
align-self: stretch;
}
}
</style>
Keyboard
Focus the marker (with Tab) to adjust the color with the keyboard:
- ← / → nudge the hue.
- ↑ / ↓ nudge the radial channel (when
channelis set). - Hold Shift for a ×10 step.
A readonly wheel’s marker stays focusable (so its value is exposed to assistive tech) but can’t be changed. The radial channel isn’t yet a separate slider for assistive tech; its value is announced as part of the marker’s value text.
Notes
spacemust be polar (have a hue/angle coordinate). Thespacesetter throws for a non-polar space; a non-polarspaceattribute is ignored with a console warning, keeping the previous space.- The marker color, the painted field, and the radial axis are all expressed in
space.coloris always returned inspace. - For color spaces the browser can’t interpolate gradients in, the hue field is tessellated and interpolated in
oklch, so the wheel still renders smoothly.
Reference
Slots
| Name | Description |
|---|---|
| (default) | Overlay content rendered on top of the wheel (used to build <gamut-wheel>). Slotted <color-swatch> / <color-scale> elements are also plotted as extra points. |
marker |
The draggable marker for the current color. Defaults to a built-in dot; slot your own <color-swatch slot="marker"> to fully control its appearance (and, e.g., add a details tooltip). hue-wheel sets its color, positions it, and makes it the slider. |
Attributes & Properties
| Attribute | Property | Property type | Default value | Description |
|---|---|---|---|---|
space |
spaceId / space |
string / ColorSpace |
oklch |
The polar color space of the wheel. The attribute and the space property accept a space id or ColorSpace; the spaceId property is always the id string. A non-polar value is rejected (the space setter throws; a non-polar attribute is ignored with a warning). |
channel |
channel |
string | null |
null |
Coordinate shown on the radial axis. When omitted, the wheel shows only hue (as a ring). |
color |
color |
Color | null |
null |
The current color (always in space). null when no color is set. |
| - | hueValue |
number |
- | The current hue value. Read/write. |
| - | channelValue |
number | undefined |
- | The current channel value, if channel is set. Read/write. |
readonly |
readonly |
boolean |
false |
When set, the color cannot be added or edited. |
Events
| Name | Description |
|---|---|
input |
Fired continuously while the color is being changed (drag, click, arrow keys). |
change |
Fired when a change is committed (pointer release, or each arrow keypress). |
colorchange |
Fired whenever the color property changes, for any reason. |
CSS variables
| Variable | Type | Description |
|---|---|---|
--size |
<length> |
The diameter of the wheel. Defaults to 320px. |
--ring-thickness |
<number> |
In ring mode (no channel), the ring’s thickness as a fraction of the radius. Defaults to 0.35. |
--dot-size |
<length> |
The diameter of the plotted points (passed through to their <color-swatch> dots). Defaults to 1em. |
Parts
| Name | Description |
|---|---|
wheel |
The circular wheel container. |
disc |
The painted hue/channel field. |
marker |
The draggable marker showing the current color. |
Installation
To install all color elements, check out the instructions on the homepage.
The rest of this section is about using only <hue-wheel>.
If you’re using a bundler, or an import map manager like Nudeps or JSPM, you can import it via a bare specifier:
import "color-elements/hue-wheel";
or:
import { HueWheel } from "color-elements";
Otherwise, you can use a CDN such as esm.sh:
<script src="https://esm.sh/color-elements/hue-wheel" type="module"></script>