Color Picker
Accessible, unstyled color picker: 2D area, channel sliders, swatches, text field, and eye dropper.
| Package | @vizejs/ui/color-picker |
| Maturity | stable |
| Own the source | vize lib pull color-picker |
| Requires | collection, composite-navigation, context, controllable-state, id |
| Aliases | color picker, colour picker, color input, hue slider, eye dropper |
| Covers | HTML input type=color, React Aria ColorPicker, ColorArea, ColorSlider, ColorSwatchPicker, ColorField, Ark UI ColorPicker, EyeDropper API, CSS Color Module Level 4 rgb()/hsl() syntax |
Usage
import { ColorPicker, ColorPickerRoot, ColorPickerArea, ColorPickerChannelSlider, ColorPickerEyeDropper, ColorPickerField, ColorPickerSwatch, ColorPickerSwatchGroup } from "@vizejs/ui/color-picker";
Or copy the source into your project with vize lib pull color-picker (see Source Distribution).
API
ColorPicker
Source: src/families/form/color-picker/color-picker-root.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
id |
string | null |
undefined |
Consumer-owned base id. null and undefined select a deterministic fallback. |
modelValue |
string |
undefined |
Controlled color as any CSS color string accepted by parseColor. undefined selects uncontrolled behavior. Unparseable strings keep the last valid color. |
defaultValue |
string |
"#000000" |
Initial uncontrolled color, also restored by reset() and native form reset. |
format |
ColorFormat |
"hex" |
Serialization used for update:modelValue, slot value, and the form value. |
disabled |
boolean |
false |
Remove every part from interaction and sequential focus. |
readOnly |
boolean |
false |
Keep parts focusable while rejecting edits. |
required |
boolean |
false |
Mark the form value as required (forwarded to the hidden input's data-required). |
name |
string |
undefined |
Native form field name. When set, a hidden input submits the formatted value. |
form |
string |
undefined |
Id of a form owner outside the root's DOM ancestry. |
dir |
ColorPickerDirection |
"ltr" |
Reading direction used for horizontal pointer and arrow-key mapping. |
messages |
ColorPickerMessages |
undefined |
Localized accessible strings (channel names, value text, area name). Omitted entries use English defaults. |
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue |
[value: string] |
Fired with the formatted value whenever the color changes. |
change |
[value: string, detail: ColorPickerChangeDetail] |
Fired after every distinct color change with the normalized color and its source. |
commit |
[value: string, color: ColorValue] |
Fired when a continuous interaction (drag end, key press, field commit, pick) settles. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
ColorPickerSlotState |
Compound ColorPicker parts. Receives the current color and availability state. |
Exposed
| Member | Type | Description |
|---|---|---|
color |
— | |
disabled |
— | |
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
format |
— | |
id |
— | |
readOnly |
— | |
reset |
— | |
setColor |
— | |
state |
— | |
value |
— |
ColorPickerRoot
Source: src/families/form/color-picker/color-picker-root.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
id |
string | null |
undefined |
Consumer-owned base id. null and undefined select a deterministic fallback. |
modelValue |
string |
undefined |
Controlled color as any CSS color string accepted by parseColor. undefined selects uncontrolled behavior. Unparseable strings keep the last valid color. |
defaultValue |
string |
"#000000" |
Initial uncontrolled color, also restored by reset() and native form reset. |
format |
ColorFormat |
"hex" |
Serialization used for update:modelValue, slot value, and the form value. |
disabled |
boolean |
false |
Remove every part from interaction and sequential focus. |
readOnly |
boolean |
false |
Keep parts focusable while rejecting edits. |
required |
boolean |
false |
Mark the form value as required (forwarded to the hidden input's data-required). |
name |
string |
undefined |
Native form field name. When set, a hidden input submits the formatted value. |
form |
string |
undefined |
Id of a form owner outside the root's DOM ancestry. |
dir |
ColorPickerDirection |
"ltr" |
Reading direction used for horizontal pointer and arrow-key mapping. |
messages |
ColorPickerMessages |
undefined |
Localized accessible strings (channel names, value text, area name). Omitted entries use English defaults. |
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue |
[value: string] |
Fired with the formatted value whenever the color changes. |
change |
[value: string, detail: ColorPickerChangeDetail] |
Fired after every distinct color change with the normalized color and its source. |
commit |
[value: string, color: ColorValue] |
Fired when a continuous interaction (drag end, key press, field commit, pick) settles. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
ColorPickerSlotState |
Compound ColorPicker parts. Receives the current color and availability state. |
Exposed
| Member | Type | Description |
|---|---|---|
value |
string |
Current value serialized in the root format. |
color |
ColorValue |
Current normalized color. |
format |
ColorFormat |
Serialization used for value, the hidden form input, and update:modelValue. |
disabled |
boolean |
Whether every part suppresses interaction and focus. |
readOnly |
boolean |
Whether parts stay focusable but reject edits. |
state |
ColorPickerState |
Stable state token for styling and tests. |
element |
HTMLDivElement | null |
Rendered root element. |
id |
string |
Root-owned deterministic base id. |
setColor |
(color: ColorValue | string) => boolean |
Request a color (object or CSS string) and report whether it changed. Invalid strings are ignored. |
reset |
() => boolean |
Restore the default value and report whether it changed. |
ColorPickerArea
Source: src/families/form/color-picker/color-picker-area.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
xChannel |
ColorChannel |
"saturation" |
Channel mapped to the horizontal axis (inline start = minimum). |
yChannel |
ColorChannel |
"brightness" |
Channel mapped to the vertical axis (bottom = minimum). |
space |
ColorSpace |
"hsb" |
Color space used to resolve hue, saturation, and alpha channels. |
ariaLabel |
string |
undefined |
Accessible name of the 2D slider thumb. Defaults to "<X> and <Y>". |
ariaLabelledby |
string |
undefined |
Space-separated ids that label the thumb. |
ariaDescribedby |
string |
undefined |
Space-separated ids that describe the thumb. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
ColorPickerAreaSlotState |
Thumb contents. Receives both channel values and thumb position. |
Exposed
| Member | Type | Description |
|---|---|---|
element |
HTMLDivElement | null |
Rendered area element. |
thumb |
HTMLDivElement | null |
Rendered focusable thumb element. |
focus |
(options?: FocusOptions) => void |
Move focus to the thumb. |
ColorPickerChannelSlider
Source: src/families/form/color-picker/color-picker-channel-slider.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
channel (required) |
ColorChannel |
required |
Channel edited by this slider, e.g. "hue" or "alpha". |
space |
ColorSpace |
"hsb" |
Color space used to resolve hue, saturation, and alpha. |
orientation |
ColorPickerOrientation |
"horizontal" |
Layout axis. Vertical sliders place the minimum at the bottom. |
step |
number |
undefined |
Arrow-key and snapping increment. Defaults to the channel's natural step. |
ariaLabel |
string |
undefined |
Accessible name. Defaults to the English channel label, e.g. "Hue". |
ariaLabelledby |
string |
undefined |
Space-separated ids that label the thumb. |
ariaDescribedby |
string |
undefined |
Space-separated ids that describe the thumb. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
ColorPickerChannelSliderSlotState |
Thumb contents. Receives the channel value and thumb position. |
Exposed
| Member | Type | Description |
|---|---|---|
element |
HTMLDivElement | null |
Rendered track element. |
thumb |
HTMLDivElement | null |
Rendered focusable thumb element. |
value |
number |
Current snapped channel value. |
focus |
(options?: FocusOptions) => void |
Move focus to the thumb. |
ColorPickerEyeDropper
Source: src/families/form/color-picker/color-picker-eye-dropper.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
unsupported |
ColorPickerEyeDropperFallback |
"disable" |
Rendering when the EyeDropper API is unavailable (and always during SSR): "disable" keeps a disabled button, "hide" adds the hidden attribute. |
preserveAlpha |
boolean |
true |
Keep the current alpha; the system picker always returns an opaque color. |
ariaLabel |
string |
undefined |
Accessible name when the default slot has no text. |
Events
| Event | Payload | Description |
|---|---|---|
pick |
[color: ColorValue, sRGBHex: string] |
Fired with the picked color after it has been applied. |
cancel |
[] |
Fired when the user dismisses the system picker. |
error |
[error: unknown] |
Fired when the picker rejects for a reason other than dismissal. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
ColorPickerEyeDropperSlotState |
Button contents. Receives support and picking state. |
Exposed
| Member | Type | Description |
|---|---|---|
supported |
boolean |
Whether the browser provides the EyeDropper API (always false during SSR). |
picking |
boolean |
Whether the system picker is open. |
state |
ColorPickerEyeDropperState |
Stable state token for styling and tests. |
element |
HTMLButtonElement | null |
Rendered native button. |
open |
() => Promise<ColorValue | null> |
Open the system picker. Resolves with the picked color, or null when cancelled or unsupported. |
ColorPickerField
Source: src/families/form/color-picker/color-picker-field.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
format |
ColorFormat |
undefined |
Serialization shown in the field. Defaults to the root format. |
preserveAlpha |
boolean |
false |
Keep the current alpha when the typed color has no alpha component (for example typing #ff0000 into a half-transparent picker). |
ariaLabel |
string |
undefined |
Accessible name when no <label> or aria-labelledby supplies one. |
ariaLabelledby |
string |
undefined |
Space-separated ids that label the field. |
ariaDescribedby |
string |
undefined |
Space-separated ids that describe the field. |
placeholder |
string |
undefined |
Native placeholder text. |
Events
| Event | Payload | Description |
|---|---|---|
invalid |
[draft: string, nativeEvent: Event | null] |
Fired when a committed draft cannot be parsed and the field reverts. |
Exposed
| Member | Type | Description |
|---|---|---|
element |
HTMLInputElement | null |
Rendered native text input. |
draft |
string |
Current draft text. |
state |
ColorPickerFieldState |
Validity of the draft text. |
commit |
() => boolean |
Parse and commit the draft; invalid drafts revert. Returns whether it parsed. |
revert |
() => void |
Discard the draft and show the current color. |
focus |
(options?: FocusOptions) => void |
Move focus to the input. |
ColorPickerSwatch
Source: src/families/form/color-picker/color-picker-swatch.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
value (required) |
string |
required |
CSS color selected by this swatch, e.g. "#ff0000" or "hsl(210 80% 40%)". |
disabled |
boolean |
false |
Disable this swatch while keeping it visible. |
label |
string |
undefined |
Accessible name. Defaults to the color serialized in the root format. |
order |
number |
undefined |
Deterministic order for virtualized or portalled swatches. |
Events
| Event | Payload | Description |
|---|---|---|
click |
[nativeEvent: MouseEvent] |
Fired before selection. Call preventDefault() to keep the current color. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
ColorPickerSwatchSlotState |
Swatch contents, e.g. a check mark. Receives selection state. |
Exposed
| Member | Type | Description |
|---|---|---|
value |
string |
Swatch color string exactly as supplied. |
color |
ColorValue |
Parsed swatch color. |
checked |
boolean |
Whether the swatch matches the root color. |
disabled |
boolean |
Whether the swatch or root suppresses selection. |
state |
ColorPickerSwatchState |
Stable state token for styling and tests. |
element |
HTMLDivElement | null |
Rendered swatch element. |
focus |
(options?: FocusOptions) => void |
Move focus to the swatch. |
ColorPickerSwatchGroup
Source: src/families/form/color-picker/color-picker-swatch-group.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
loop |
boolean |
true |
Whether arrow-key navigation wraps at the first and last enabled swatch. |
ariaLabel |
string |
undefined |
Accessible name of the radiogroup, e.g. "Saved colors". |
ariaLabelledby |
string |
undefined |
Space-separated ids that label the radiogroup. |
ariaDescribedby |
string |
undefined |
Space-separated ids that describe the radiogroup. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
ColorPickerSlotState |
ColorPickerSwatch children. Receives the root color state. |
Exposed
| Member | Type | Description |
|---|---|---|
element |
HTMLDivElement | null |
Rendered radiogroup element. |
focus |
(options?: FocusOptions) => void |
Move focus to the checked, active, or first enabled swatch. |
Behavior
Normative state x input -> outcome table for color-picker-root.vue,
color-picker-area.vue, color-picker-channel-slider.vue,
color-picker-swatch-group.vue, color-picker-swatch.vue,
color-picker-field.vue, and color-picker-eye-dropper.vue
(@vizejs/ui/color-picker), plus the pure color model in
color-picker-color.ts. Every row is proven by the named test.
Value model
v-model is a CSS color string. Any syntax accepted by parseColor may be
supplied (#rgb[a], #rrggbb[aa], rgb()/rgba() and hsl()/hsla() in
legacy comma or CSS Color 4 space/slash syntax, hue units deg/grad/rad/
turn, the non-CSS hsb(), and transparent); emitted values are serialized
in the root format (hex by default, which becomes #rrggbbaa below full
opacity). Internally the root keeps the last accepted color as an immutable
HSB ColorValue, so hue survives greys and saturation survives black while a
user drags. The typed ColorValue is available through change details, slot
state, and setColor. Unparseable model strings keep the last valid color.
Table
| ID | State | Input | Outcome | Evidence |
|---|---|---|---|---|
| CP1 | model | parse hex | 3/4/6/8-digit hex parses case-insensitively; malformed hex returns null |
parses every hex notation and normalizes to lowercase |
| CP2 | model | parse rgb | legacy and modern rgb, percentages, and alpha parse; channels clamp like CSS; malformed argument lists return null |
parses legacy and modern rgb syntax with percentages and alpha |
| CP3 | model | parse hsl / hsb | hue units deg/grad/rad/turn and negative hues wrap; hsb notation parses; bad units return null |
parses hsl with angle units and hsb notation |
| CP4 | model | parse keyword / empty | transparent parses; empty and unsupported named colors return null |
parses transparent and rejects empty or unknown input |
| CP5 | model | format | every ColorFormat serializes with and without alpha; hex is the default |
formats every color format with and without alpha |
| CP6 | model | round-trip | 8-bit colors round-trip losslessly through hex, hex8, rgb, and hsb | round-trips every 8-bit primary and secondary through each format |
| CP7 | model | convert | hsb ↔ rgb ↔ hsl conversions are exact | converts between hsb, rgb, and hsl exactly |
| CP8 | model | achromatic convert | greys keep the fallback hue; black keeps the fallback saturation | achromatic conversions keep the fallback hue and saturation |
| CP9 | model | create | hue wraps, channels clamp, non-finite channels fall back, results are frozen | createColor wraps hue and clamps every channel |
| CP10 | model | channel get/set | every channel reads and writes in its space; out-of-range values clamp | reads and writes every channel in its color space |
| CP11 | model | channel metadata | space resolution, ranges, labels, snapping, and aria-valuetext strings are stable |
resolves channel spaces, ranges, labels, snapping, and value text |
| CP12 | model | gradient | channel track gradients interpolate exactly (hue uses six segments, lightness three stops) | builds exact channel preview gradients |
| CP13 | model | equality / guards | equality is 8-bit sRGB plus 1/255 alpha; isColorValue / isColorFormat narrow unknown input |
compares colors at 8-bit precision and guards unknown values |
| CP14 | helpers | pointer / keys / platform | pointer fractions clamp and flip in RTL; key intents cover arrows, Shift, Page keys, Home/End; capture and EyeDropper helpers tolerate missing APIs; swatch ids are DOM-safe | maps pointer coordinates to clamped fractions in both directions, maps fractions and values across channel ranges, resolves one- and two-dimensional keyboard intents, pointer capture helpers tolerate missing or throwing platform APIs, eye dropper helpers feature-detect and validate untyped platform values, creates DOM-id-safe swatch segments for arbitrary color strings |
| CP15 | root default | render | role="group" root exposes id, data-state/value, --vize-ui-color-picker-* custom properties, slot state, and a hidden form input |
renders a labelled root with color custom properties, slot state, and form value |
| CP16 | area | pointer down / move / up | pointer position maps to saturation (x) and brightness (y, bottom = 0), focuses the thumb, publishes thumb vars, commits on release | area maps pointer drags to saturation and brightness with a committed release |
| CP17 | area | Arrow / Shift+Arrow / Page / Home / End | one role="slider" thumb with aria-roledescription="2D slider" and combined aria-valuetext; each key edits one axis and commits |
area thumb exposes 2D slider semantics and keyboard steps |
| CP18 | area, custom axes | keys through grey | hue survives achromatic intermediates; any channel pair (e.g. hue × lightness) can drive the area | area keeps the hue while dragging through greys and supports custom channels |
| CP19 | channel slider | keys / pointer | hue, alpha (vertical), and rgb channels step, page, jump, and follow pointer position; track gradient and percent vars update | channel sliders edit hue, alpha, and rgb channels with keyboard and pointer |
| CP20 | dir="rtl" |
ArrowRight / pointer | horizontal arrows and pointer mapping invert | right-to-left layouts invert horizontal arrows and pointer mapping |
| CP21 | swatch group | Tab / Arrow / click / Space | radiogroup of radio swatches with roving tabindex; arrows move and select skipping disabled swatches; click and Space select |
swatches form a roving radiogroup that selects on click and arrow keys |
| CP22 | swatch | click with preventDefault() |
selection is cancelled | swatch click can be cancelled before selection |
| CP23 | field | type / Enter / blur / Escape / commit() | drafts validate live (aria-invalid), Enter and blur commit parseable text, invalid commits emit invalid and revert, Escape reverts |
field parses on Enter and blur, flags invalid drafts, and reverts them |
| CP24 | field preserveAlpha |
commit opaque text | the current alpha is kept and the field can use its own format | field can preserve the current alpha and follow its own format |
| CP25 | controlled | edit | emits the requested value while rendering the controlled color until the parent accepts it; invalid model keeps the last color | controlled value wins until the parent accepts the request |
| CP26 | disabled / read-only | keys / pointer / Tab | disabled removes thumbs from the tab order and disables inputs; read-only stays focusable, marks aria-readonly, rejects edits |
disabled and read-only roots suppress edits while read-only stays focusable |
| CP27 | exposed root | setColor / reset | exposes value, color, format, state, element; setColor accepts strings or ColorValue and rejects invalid strings |
exposes typed state and imperative setColor/reset controls |
| CP28 | inside <form> |
native reset | uncontrolled roots restore defaultValue and the submitted value |
native form reset restores the default value |
| CP29 | eye dropper supported | click / open() | opens the system picker, applies the sRGB result (keeping alpha by default), emits pick; AbortError emits cancel, others error |
eye dropper feature-detects, picks, preserves alpha, and reports cancel |
| CP30 | eye dropper unsupported | render | data-state="unsupported"; unsupported="disable" disables, "hide" adds hidden |
eye dropper disables or hides itself without platform support |
| CP31 | missing provider | setup | parts fail closed with the shared context diagnostic | compound parts require a matching root provider |
| CP32 | SSR and hydration | render twice / hydrate | markup is byte-identical per request (eye dropper renders unsupported on the server) and hydrates without warnings or id changes | renders byte-identical color picker markup across isolated SSR requests, hydrates generated color picker ids without changing the server contract |
| CP33 | messages |
render | channel names, aria-valuetext, and the 2D area name come from the typed messages prop; omitted entries use English defaults |
messages localize channel names, value text, and the area name |
Notes
- Area keyboard map: Left/Right edit the x channel, Up/Down the y channel,
Shift multiplies to the channel page step, Page Up/Down page the y channel,
Home/End jump the x channel. Horizontal keys invert under
dir="rtl". - Default accessible names are English (
"Hue","Saturation and Brightness"); passariaLabel/ariaLabelledbyto localize. commitfires once per settled interaction (pointer release, each key press, field commit, swatch selection, eye-dropper pick);changefires for every intermediate color.- No CSS ships. Style from
data-*hooks and the published custom properties:--vize-ui-color-picker-color,-opaque-color,-hue,-alpha(root),-thumb-x,-thumb-y,-area-hue,-area-background(area),-thumb-percent,-track-background(slider),-swatch-color(swatch).