Color Picker
The color picker is an input widget used to select a color value from a predefined list or a color area.
This component builds on top of the native <input type=color> experience and
provides a more customizable and consistent user experience.
Features
- Supports custom color area
- Supports RGBA, HSLA, HEX, and HSBA formats
- Supports channel inputs and sliders
- Supports mouse, touch, and keyboard interactions
- Supports form submission and reset events
- Supports named CSS colors
Installation
Install the color picker package:
npm install @zag-js/color-picker @zag-js/react # or yarn add @zag-js/color-picker @zag-js/react
npm install @zag-js/color-picker @zag-js/solid # or yarn add @zag-js/color-picker @zag-js/solid
npm install @zag-js/color-picker @zag-js/vue # or yarn add @zag-js/color-picker @zag-js/vue
npm install @zag-js/color-picker @zag-js/svelte # or yarn add @zag-js/color-picker @zag-js/svelte
Anatomy
To set up the color picker correctly, you'll need to understand its anatomy and how we name its parts.
Each part includes a
data-partattribute to help identify them in the DOM.
Usage
Import the color picker package:
import * as colorPicker from "@zag-js/color-picker"
These are the key exports:
machine- Behavior logic for the color picker.connect- Maps state to JSX props and event handlers.parse- Parses a color string into aColorobject.
Then use the framework integration helpers:
import * as colorPicker from "@zag-js/color-picker" import { normalizeProps, useMachine } from "@zag-js/react" import { useId } from "react" function ColorPicker() { const service = useMachine(colorPicker.machine, { id: useId(), defaultValue: colorPicker.parse("hsl(0, 100%, 50%)"), }) const api = colorPicker.connect(service, normalizeProps) return ( <div {...api.getRootProps()}> <label {...api.getLabelProps()}>Select Color: {api.valueAsString}</label> <input {...api.getHiddenInputProps()} /> <div {...api.getControlProps()}> <button {...api.getTriggerProps()}> <div {...api.getTransparencyGridProps({ size: "10px" })} /> <div {...api.getSwatchProps({ value: api.value })} /> </button> <input {...api.getChannelInputProps({ channel: "hex" })} /> <input {...api.getChannelInputProps({ channel: "alpha" })} /> </div> <div {...api.getPositionerProps()}> <div {...api.getContentProps()}> <div {...api.getAreaProps()}> <div {...api.getAreaBackgroundProps()} /> <div {...api.getAreaThumbProps()} /> </div> <div {...api.getChannelSliderProps({ channel: "hue" })}> <div {...api.getChannelSliderTrackProps({ channel: "hue" })} /> <div {...api.getChannelSliderThumbProps({ channel: "hue" })} /> </div> <div {...api.getChannelSliderProps({ channel: "alpha" })}> <div {...api.getTransparencyGridProps({ size: "12px" })} /> <div {...api.getChannelSliderTrackProps({ channel: "alpha" })} /> <div {...api.getChannelSliderThumbProps({ channel: "alpha" })} /> </div> </div> </div> </div> ) }
import * as colorPicker from "@zag-js/color-picker" import { normalizeProps, useMachine } from "@zag-js/solid" import { createMemo, createUniqueId } from "solid-js" function ColorPicker() { const service = useMachine(colorPicker.machine, { id: createUniqueId(), defaultValue: colorPicker.parse("hsl(0, 100%, 50%)"), }) const api = createMemo(() => colorPicker.connect(service, normalizeProps)) return ( <div {...api().getRootProps()}> <label {...api().getLabelProps()}> Select Color: {api.valueAsString} </label> <input {...api().getHiddenInputProps()} /> <div {...api().getControlProps()}> <button {...api().getTriggerProps()}> <div {...api().getTransparencyGridProps({ size: "10px" })} /> <div {...api().getSwatchProps({ value: api.value })} /> </button> <input {...api().getChannelInputProps({ channel: "hex" })} /> <input {...api().getChannelInputProps({ channel: "alpha" })} /> </div> <div {...api().getPositionerProps()}> <div {...api().getContentProps()}> <div {...api().getAreaProps()}> <div {...api().getAreaBackgroundProps()} /> <div {...api().getAreaThumbProps()} /> </div> <div {...api().getChannelSliderProps({ channel: "hue" })}> <div {...api().getChannelSliderTrackProps({ channel: "hue" })} /> <div {...api().getChannelSliderThumbProps({ channel: "hue" })} /> </div> <div {...api().getChannelSliderProps({ channel: "alpha" })}> <div {...api().getTransparencyGridProps({ size: "12px" })} /> <div {...api().getChannelSliderTrackProps({ channel: "alpha" })} /> <div {...api().getChannelSliderThumbProps({ channel: "alpha" })} /> </div> </div> </div> </div> ) }
<script setup> import * as colorPicker from "@zag-js/color-picker" import { normalizeProps, useMachine } from "@zag-js/vue" import { computed } from "vue" const service = useMachine(colorPicker.machine, { id: "1", defaultValue: colorPicker.parse("hsl(0, 100%, 50%)"), }) const api = computed(() => colorPicker.connect(service, normalizeProps)) </script> <template> <div v-bind="api.getRootProps()"> <label v-bind="api.getLabelProps()" >Select Color: {{ api.valueAsString }}</label > <input v-bind="api.getHiddenInputProps()" /> <div v-bind="api.getControlProps()"> <button v-bind="api.getTriggerProps()"> <div v-bind="api.getTransparencyGridProps({ size: '10px' })" /> <div v-bind="api.getSwatchProps({ value: api.value })" /> </button> <input v-bind="api.getChannelInputProps({ channel: 'hex' })" /> <input v-bind="api.getChannelInputProps({ channel: 'alpha' })" /> </div> <div v-bind="api.getPositionerProps()"> <div v-bind="api.getContentProps()"> <div v-bind="api.getAreaProps()"> <div v-bind="api.getAreaBackgroundProps()" /> <div v-bind="api.getAreaThumbProps()" /> </div> <div v-bind="api.getChannelSliderProps({ channel: 'hue' })"> <div v-bind="api.getChannelSliderTrackProps({ channel: 'hue' })" /> <div v-bind="api.getChannelSliderThumbProps({ channel: 'hue' })" /> </div> <div v-bind="api.getChannelSliderProps({ channel: 'alpha' })"> <div v-bind="api.getTransparencyGridProps({ size: '12px' })" /> <div v-bind="api.getChannelSliderTrackProps({ channel: 'alpha' })" /> <div v-bind="api.getChannelSliderThumbProps({ channel: 'alpha' })" /> </div> </div> </div> </div> </template>
<script lang="ts"> import * as colorPicker from "@zag-js/color-picker" import { normalizeProps, useMachine } from "@zag-js/svelte" const id = $props.id() const service = useMachine(colorPicker.machine, ({ id })) const api = $derived(colorPicker.connect(service, normalizeProps)) </script> <div {...api.getRootProps()}> <label {...api.getLabelProps()}>Select Color: {api.valueAsString}</label> <input {...api.getHiddenInputProps()} /> <div {...api.getControlProps()}> <button {...api.getTriggerProps()}> <div {...api.getTransparencyGridProps({ size: "10px" })}></div> <div {...api.getSwatchProps({ value: api.value })}></div> </button> <input {...api.getChannelInputProps({ channel: "hex" })} /> <input {...api.getChannelInputProps({ channel: "alpha" })} /> </div> <div {...api.getPositionerProps()}> <div {...api.getContentProps()}> <div {...api.getAreaProps()}> <div {...api.getAreaBackgroundProps()}></div> <div {...api.getAreaThumbProps()}></div> </div> <div {...api.getChannelSliderProps({ channel: "hue" })}> <div {...api.getChannelSliderTrackProps({ channel: "hue" })}></div> <div {...api.getChannelSliderThumbProps({ channel: "hue" })}></div> </div> <div {...api.getChannelSliderProps({ channel: "alpha" })}> <div {...api.getTransparencyGridProps({ size: "12px" })}></div> <div {...api.getChannelSliderTrackProps({ channel: "alpha" })}></div> <div {...api.getChannelSliderThumbProps({ channel: "alpha" })}></div> </div> </div> </div> </div>
Setting the initial color
Set defaultValue to define the initial color.
const [current, send] = useMachine(colorPicker.machine, { defaultValue: colorPicker.parse("#ff0000"), })
Controlled color picker
Use value and onValueChange to control color externally.
Note: We recommend preserving the value as a
Colorobject rather than a string to prevent calculation errors by converting back and forth.
import { useState } from "react" import * as colorPicker from "@zag-js/color-picker" export function ControlledColorPicker() { const [value, setValue] = useState(colorPicker.parse("#ff0000")) const service = useMachine(colorPicker.machine, { value, onValueChange(details) { setValue(details.value) } }) return ( // ... ) }
import { createSignal } from "solid-js" import * as colorPicker from "@zag-js/color-picker" export function ControlledColorPicker() { const [value, setValue] = createSignal(colorPicker.parse("#ff0000")) const service = useMachine(colorPicker.machine, { get value() { return value() }, onValueChange(details) { setValue(details.value) }, }) return ( // ... ) }
<script setup lang="ts"> import { ref } from "vue" import * as colorPicker from "@zag-js/color-picker" const valueRef = ref(colorPicker.parse("#ff0000")) const service = useMachine(colorPicker.machine, { get value() { return valueRef.value }, onValueChange(details) { valueRef.value = details.value }, }) </script>
<script lang="ts"> import * as colorPicker from "@zag-js/color-picker" let value = $state(colorPicker.parse("#ff0000")) const service = useMachine(colorPicker.machine, { get value() { return value }, onValueChange(details) { value = details.value }, }) </script>
Listening for change events
When the user selects a color using the color picker, the onValueChange and
onValueChangeEnd events will be fired.
onValueChange— Fires in sync as the user selects a coloronValueChangeEnd— Fires when the user stops selecting a color (useful for debounced updates)
const [current, send] = useMachine(colorPicker.machine, { onValueChange: (details) => { // details => { value: Color, valueAsString: string } }, onValueChangeEnd: (details) => { // details => { value: Color, valueAsString: string } }, })
Using a custom color format
By default, the color picker's output format is rgba. You can change this
format to either hsla or hsba with format.
When this is set, the value and valueAsString properties of the
onValueChange event will be updated to reflect the new format.
const [current, send] = useMachine(colorPicker.machine, { format: "hsla", onValueChange: (details) => { // details => { value: HSLAColor, valueAsString: string } }, })
Setting the initial format
Use defaultFormat to set the initial output format.
const [current, send] = useMachine(colorPicker.machine, { defaultFormat: "hsla", })
Controlled format
Use format and onFormatChange to control the active format.
const [current, send] = useMachine(colorPicker.machine, { format, onFormatChange(details) { setFormat(details.format) }, })
Showing color presets
Adding color presets as swatches can help users pick colors faster. To support
this, use the getSwatchTriggerProps(...) and getSwatchProps(...) to get the
props needed to render swatch buttons.
const ColorPicker = () => { const service = useMachine(colorPicker.machine, { id: useId(), defaultValue: colorPicker.parse("hsl(0, 100%, 50%)"), }) const api = colorPicker.connect(service, normalizeProps) const presets = ["#ff0000", "#00ff00", "#0000ff"] return ( <div {...api.getRootProps()}> {/* ... */} <div {...api.getPositionerProps()}> <div {...api.getContentProps()}> <div {...api.getSwatchGroupProps()}> {presets.map((preset) => ( <button key={preset} {...api.getSwatchTriggerProps({ value: preset })} > <div style={{ position: "relative" }}> <div {...api.getTransparencyGridProps({ size: "4px" })} /> <div {...api.getSwatchProps({ value: preset })} /> </div> </button> ))} </div> </div> </div> </div> ) }
const ColorPicker = () => { const service = useMachine(colorPicker.machine, { id: createUniqueId(), defaultValue: colorPicker.parse("hsl(0, 100%, 50%)"), }) const api = createMemo(() => colorPicker.connect(service, normalizeProps)) const presets = ["#ff0000", "#00ff00", "#0000ff"] return ( <div {...api().getRootProps()}> {/* ... */} <div {...api().getPositionerProps()}> <div {...api().getContentProps()}> <div {...api().getSwatchGroupProps()}> <Index each={preset}> {(preset) => ( <button {...api().getSwatchTriggerProps({ value: preset() })}> <div style={{ position: "relative" }}> <div {...api().getTransparencyGridProps({ size: "4px" })} /> <div {...api().getSwatchProps({ value: preset() })} /> </div> </button> )} </Index> </div> </div> </div> </div> ) }
<script setup> import * as colorPicker from "@zag-js/color-picker" import { normalizeProps, useMachine } from "@zag-js/vue" import { computed, ref } from "vue" const service = useMachine(colorPicker.machine, { id: "1", defaultValue: colorPicker.parse("hsl(0, 100%, 50%)"), }) const api = computed(() => colorPicker.connect(service, normalizeProps)) const presets = ref(["#ff0000", "#00ff00", "#0000ff"]) </script> <template> <div v-bind="api.getRootProps()"> // ... <div v-bind="api.getPositionerProps()"> <div v-bind="api.getContentProps()"> <div v-bind="api.getSwatchGroupProps()"> <template v-for="preset in presets"> <button v-bind="api.getSwatchTriggerProps({ value: preset })"> <div v-bind="api.getTransparencyGridProps({ size: '4px' })" /> <div v-bind="api.getSwatchProps({ value: preset })" /> </button> </template> </div> </div> </div> </div> </template>
<script lang="ts"> import * as colorPicker from "@zag-js/color-picker" import { normalizeProps, useMachine } from "@zag-js/svelte" const id = $props.id() const service = useMachine(colorPicker.machine, { id, defaultValue: colorPicker.parse("hsl(0, 100%, 50%)"), }) const api = $derived(colorPicker.connect(service, normalizeProps)) const presets = ["#ff0000", "#00ff00", "#0000ff"] </script> <div {...api.getRootProps()}> <!-- ... --> <div {...api.getPositionerProps()}> <div {...api.getContentProps()}> <div {...api.getSwatchGroupProps()}> {#each presets as preset} <button {...api.getSwatchTriggerProps({ value: preset })}> <div style="position:relative;"> <div {...api.getTransparencyGridProps({ size: "4px" })}></div> <div {...api.getSwatchProps({ value: preset })}></div> </div> </button> {/each} </div> </div> </div> </div>
Closing after swatch selection
Set closeOnSelect to close the popup when a swatch is selected.
const [current, send] = useMachine(colorPicker.machine, { closeOnSelect: true, })
Disabling the color picker
Set disabled to true to disable user interaction.
const [current, send] = useMachine(colorPicker.machine, { disabled: true, })
Controlling the open and closed state
Use open and onOpenChange to control whether the picker is visible.
const [current, send] = useMachine(colorPicker.machine, { open: true, onOpenChange: (details) => { // details => { open: boolean, value: Color } }, })
You can also leverage the api.setOpen(...) method to control the open and
closed state of the color picker.
Positioning the popup
Use positioning to control popup placement and collision behavior.
const [current, send] = useMachine(colorPicker.machine, { positioning: { placement: "bottom-start", gutter: 8 }, })
Setting initial focus when opened
Use initialFocusEl to choose which element receives focus when the popup
opens.
const [current, send] = useMachine(colorPicker.machine, { initialFocusEl: () => document.getElementById("alpha-input"), })
Inline rendering
Set inline to render the picker without a trigger and popup.
const [current, send] = useMachine(colorPicker.machine, { inline: true, })
Controlling individual color channel
In some cases, you may want to allow users to control the values of each color channel individually. You can do this using an input element or a slider element, or both.
To support this, use the getChannelInputProps(...) to show the channel inputs.
Note: Make sure you only render the channel inputs that match the
formatof the color picker.
const ColorPicker = () => { const service = useMachine(colorPicker.machine, { id: useId(), defaultValue: colorPicker.parse("hsl(0, 100%, 50%)"), }) const api = colorPicker.connect(service, normalizeProps) return ( <div {...api.getRootProps()}> {/* ... */} <div {...api.getPositionerProps()}> <div {...api.getContentProps()}> {api.format === "rgba" && ( <div> <div> <span>R</span> <input {...api.getChannelInputProps({ channel: "red" })} /> </div> <div> <span>G</span> <input {...api.getChannelInputProps({ channel: "green" })} /> </div> <div> <span>B</span> <input {...api.getChannelInputProps({ channel: "blue" })} /> </div> <div> <span>A</span> <input {...api.getChannelInputProps({ channel: "alpha" })} /> </div> </div> )} </div> </div> </div> ) }
import { Show } from "solid-js" const ColorPicker = () => { const service = useMachine(colorPicker.machine, { id: createUniqueId(), defaultValue: colorPicker.parse("hsl(0, 100%, 50%)"), }) const api = createMemo(() => colorPicker.connect(service, normalizeProps)) return ( <div {...api().getRootProps()}> {/* ... */} <div {...api().getPositionerProps()}> <div {...api().getContentProps()}> <Show when={api().format === "rgba"}> <div> <div> <span>R</span> <input {...api().getChannelInputProps({ channel: "red" })} /> </div> <div> <span>G</span> <input {...api().getChannelInputProps({ channel: "green" })} /> </div> <div> <span>B</span> <input {...api().getChannelInputProps({ channel: "blue" })} /> </div> <div> <span>A</span> <input {...api().getChannelInputProps({ channel: "alpha" })} /> </div> </div> </Show> </div> </div> </div> ) }
<script setup> import * as colorPicker from "@zag-js/color-picker" import { normalizeProps, useMachine } from "@zag-js/vue" import { computed } from "vue" const service = useMachine(colorPicker.machine, { id: "1", defaultValue: colorPicker.parse("hsl(0, 100%, 50%)"), }) const api = computed(() => colorPicker.connect(service, normalizeProps)) </script> <template> <div v-bind="api.getRootProps()"> // ... <div v-bind="api.getPositionerProps()"> <div v-bind="api.getContentProps()"> <template v-if="api.format === 'rgba'"> <div> <div> <span>R</span> <input v-bind="api.getChannelInputProps({ channel: 'red' })" /> </div> <div> <span>G</span> <input v-bind="api.getChannelInputProps({ channel: 'green' })" /> </div> <div> <span>B</span> <input v-bind="api.getChannelInputProps({ channel: 'blue' })" /> </div> <div> <span>A</span> <input v-bind="api.getChannelInputProps({ channel: 'alpha' })" /> </div> </div> </template> </div> </div> </div> </template>
<script lang="ts"> import * as colorPicker from "@zag-js/color-picker" import { normalizeProps, useMachine } from "@zag-js/svelte" const id = $props.id() const service = useMachine(colorPicker.machine, { id, defaultValue: colorPicker.parse("hsl(0, 100%, 50%)"), }) const api = $derived(colorPicker.connect(service, normalizeProps)) </script> <div {...api.getRootProps()}> <!-- ... --> <div {...api.getPositionerProps()}> <div {...api.getContentProps()}> {#if api.format === "rgba"} <div> <div> <span>R</span> <input {...api.getChannelInputProps({ channel: "red" })} /> </div> <div> <span>G</span> <input {...api.getChannelInputProps({ channel: "green" })} /> </div> <div> <span>B</span> <input {...api.getChannelInputProps({ channel: "blue" })} /> </div> <div> <span>A</span> <input {...api.getChannelInputProps({ channel: "alpha" })} /> </div> </div> {/if} </div> </div> </div>
Showing a color preview
To display the value of a color, use the getSwatchProps(...) and pass the
color value. To show the current color value, use api.value.
const ColorPicker = () => { const service = useMachine(colorPicker.machine, { id: useId(), defaultValue: colorPicker.parse("hsl(0, 100%, 50%)"), }) const api = colorPicker.connect(service, normalizeProps) return ( <div {...api.getRootProps()}> <div> <div {...api.getTransparencyGridProps({ size: "4px" })} /> <div {...api.getSwatchProps({ value: api.value })} /> </div> {/* ... */} </div> ) }
const ColorPicker = () => { const service = useMachine(colorPicker.machine, { id: createUniqueId(), defaultValue: colorPicker.parse("hsl(0, 100%, 50%)"), }) const api = createMemo(() => colorPicker.connect(service, normalizeProps)) return ( <div {...api().getRootProps()}> <div> <div {...api().getTransparencyGridProps({ size: "4px" })} /> <div {...api().getSwatchProps({ value: api().value })} /> </div> {/* ... */} </div> ) }
<script setup> import * as colorPicker from "@zag-js/color-picker" import { normalizeProps, useMachine } from "@zag-js/vue" import { computed } from "vue" const service = useMachine(colorPicker.machine, { id: "1", defaultValue: colorPicker.parse("hsl(0, 100%, 50%)"), }) const api = computed(() => colorPicker.connect(service, normalizeProps)) </script> <template> <div v-bind="api.getRootProps()"> <div> <div v-bind="api.getTransparencyGridProps({ size: '4px' })" /> <div v-bind="api.getSwatchProps({ value: api.value })" /> </div> // ... </div> </template>
<script lang="ts"> import * as colorPicker from "@zag-js/color-picker" import { normalizeProps, useMachine } from "@zag-js/svelte" const id = $props.id() const service = useMachine(colorPicker.machine, { id, defaultValue: colorPicker.parse("hsl(0, 100%, 50%)"), }) const api = $derived(colorPicker.connect(service, normalizeProps)) </script> <div {...api.getRootProps()}> <div> <div {...api.getTransparencyGridProps({ size: "4px" })}></div> <div {...api.getSwatchProps({ value: api.value })}></div> </div> <!-- ... --> </div>
You can pass
respectAlpha: falseto show the color value without the alpha channel
Adding an eyedropper
The eye dropper tool is a native browser feature that lets users pick a color
from a current page's canvas. To support this, use the
getEyeDropperTriggerProps(...).
Note: The eye dropper tool only works in Chrome and Edge browsers
const ColorPicker = () => { const service = useMachine(colorPicker.machine, { id: useId(), defaultValue: colorPicker.parse("hsl(0, 100%, 50%)"), }) const api = colorPicker.connect(service, normalizeProps) return ( <div {...api.getRootProps()}> {/* ... */} <div {...api.getPositionerProps()}> <div {...api.getContentProps()}> <button {...api.getEyeDropperTriggerProps()}> <EyeDropIcon /> </button> </div> </div> </div> ) }
const ColorPicker = () => { const service = useMachine(colorPicker.machine, { id: createUniqueId(), defaultValue: colorPicker.parse("hsl(0, 100%, 50%)"), }) const api = createMemo(() => colorPicker.connect(service, normalizeProps)) return ( <div {...api().getRootProps()}> {/* ... */} <div {...api().getPositionerProps()}> <div {...api().getContentProps()}> <button {...api().getEyeDropperTriggerProps()}> <EyeDropIcon /> </button> </div> </div> </div> ) }
<script setup> import * as colorPicker from "@zag-js/color-picker" import { normalizeProps, useMachine } from "@zag-js/vue" import { computed } from "vue" const service = useMachine(colorPicker.machine, { id: "1", defaultValue: colorPicker.parse("hsl(0, 100%, 50%)"), }) const api = computed(() => colorPicker.connect(service, normalizeProps)) </script> <template> <div v-bind="api.getRootProps()"> // ... <div v-bind="api.getPositionerProps()"> <div v-bind="api.getContentProps()"> <button v-bind="api.getEyeDropperTriggerProps()"> <EyeDropIcon /> </button> </div> </div> </div> </template>
<script lang="ts"> import * as colorPicker from "@zag-js/color-picker" import { normalizeProps, useMachine } from "@zag-js/svelte" const id = $props.id() const service = useMachine(colorPicker.machine, { id, defaultValue: colorPicker.parse("hsl(0, 100%, 50%)"), }) const api = $derived(colorPicker.connect(service, normalizeProps)) </script> <div {...api.getRootProps()}> <!-- ... --> <div {...api.getPositionerProps()}> <div {...api.getContentProps()}> <button {...api.getEyeDropperTriggerProps()}> <EyeDropIcon /> </button> </div> </div> </div>
Usage within forms
To use the color picker in a form, set name and render the hidden input.
const service = useMachine(colorPicker.machine, { name: "color-preference", })
Styling guide
Each color picker part has a data-part attribute added to them to help you
identify and style them easily.
Open and closed state
When the color picker is open or closed, the data-state attribute is added to
the trigger, content, control parts.
[data-part="control"][data-state="open|closed"] { /* styles for control open or state */ } [data-part="trigger"][data-state="open|closed"] { /* styles for control open or state */ } [data-part="content"][data-state="open|closed"] { /* styles for control open or state */ }
Focused State
When the color picker is focused, the data-focus attribute is added to the
control and label parts.
[data-part="control"][data-focus] { /* styles for control focus state */ } [data-part="label"][data-focus] { /* styles for label focus state */ }
Disabled State
When the color picker is disabled, the data-disabled attribute is added to the
label, control, trigger and option parts.
[data-part="label"][data-disabled] { /* styles for label disabled state */ } [data-part="control"][data-disabled] { /* styles for control disabled state */ } [data-part="trigger"][data-disabled] { /* styles for trigger disabled state */ } [data-part="swatch-trigger"][data-disabled] { /* styles for item disabled state */ }
Swatch State
When a swatch's color value matches the color picker's value, the
data-state=checked attribute is added to the swatch part.
[data-part="swatch-trigger"][data-state="checked|unchecked"] { /* styles for swatch's checked state */ }
Methods and Properties
Machine Context
The color picker machine exposes the following context properties:
idsPartial<{ root: string; control: string; trigger: string; label: string; input: string; hiddenInput: string; content: string; area: string; areaGradient: string; positioner: string; formatSelect: string; areaThumb: string; channelInput: (id: string) => string; channelSliderTrack: (id: ColorChannel) => string; channelSliderThumb: (id: ColorChannel) => string; }>The ids of the elements in the color picker. Useful for composition.valueColorThe controlled color value of the color pickerdefaultValueColorThe initial color value when rendered. Use when you don't need to control the color value of the color picker.disabledbooleanWhether the color picker is disabledreadOnlybooleanWhether the color picker is read-onlyrequiredbooleanWhether the color picker is requiredinvalidbooleanWhether the color picker is invalidonValueChange(details: ValueChangeDetails) => voidHandler that is called when the value changes, as the user drags.onValueChangeEnd(details: ValueChangeDetails) => voidHandler that is called when the user stops dragging.onOpenChange(details: OpenChangeDetails) => voidHandler that is called when the user opens or closes the color picker.namestringThe name for the form inputpositioningPositioningOptionsThe positioning options for the color pickerinitialFocusEl() => HTMLElementThe initial focus element when the color picker is opened.openbooleanThe controlled open state of the color pickerdefaultOpenbooleanThe initial open state of the color picker when rendered. Use when you don't need to control the open state of the color picker.formatColorFormatThe controlled color format to usedefaultFormatColorFormatThe initial color format when rendered. Use when you don't need to control the color format of the color picker.onFormatChange(details: FormatChangeDetails) => voidFunction called when the color format changescloseOnSelectbooleanWhether to close the color picker when a swatch is selectedopenAutoFocusbooleanWhether to auto focus the color picker when it is openedinlinebooleanWhether to render the color picker inlineidstringThe unique identifier of the machine.getRootNode() => Node | ShadowRoot | DocumentA root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.dir"ltr" | "rtl"The document's text/writing direction.onPointerDownOutside(event: PointerDownOutsideEvent) => voidFunction called when the pointer is pressed down outside the componentonFocusOutside(event: FocusOutsideEvent) => voidFunction called when the focus is moved outside the componentonInteractOutside(event: InteractOutsideEvent) => voidFunction called when an interaction happens outside the component
Machine API
The color picker api exposes the following methods:
draggingbooleanWhether the color picker is being draggedopenbooleanWhether the color picker is openinlinebooleanWhether the color picker is rendered inlinevalueColorThe current color value (as a string)valueAsStringstringThe current color value (as a Color object)setValue(value: string | Color) => voidFunction to set the color valuegetChannelValue(channel: ColorChannel) => stringFunction to set the color valuegetChannelValueText(channel: ColorChannel, locale: string) => stringFunction to get the formatted and localized value of a specific channelsetChannelValue(channel: ColorChannel, value: number) => voidFunction to set the color value of a specific channelformatColorFormatThe current color formatsetFormat(format: ColorFormat) => voidFunction to set the color formatalphanumberThe alpha value of the colorsetAlpha(value: number) => voidFunction to set the color alphasetOpen(open: boolean) => voidFunction to open or close the color picker
Data Attributes
CSS Variables
Accessibility
Keyboard Interactions
- EnterWhen focus is on the trigger, opens the color picker
When focus is on a trigger of a swatch, selects the color (and closes the color picker)
When focus is on the input or channel inputs, selects the color - ArrowLeftWhen focus is on the color area, decreases the hue value of the color
When focus is on the channel sliders, decreases the value of the channel - ArrowRightWhen focus is on the color area, increases the hue value of the color
When focus is on the channel sliders, increases the value of the channel - ArrowUpWhen focus is on the color area, increases the saturation value of the color
When focus is on the channel sliders, increases the value of the channel - ArrowDownWhen focus is on the color area, decreases the saturation value of the color
When focus is on the channel sliders, decreases the value of the channel - EscCloses the color picker and moves focus to the trigger