Skip to Content
Puzzle

Color Picker

Choose a color with an interactive picker, text input, or preset swatches.

ColorPicker provides a trigger and popup for choosing a color. Users can adjust saturation and brightness, set hue and opacity, enter a CSS color value, or choose from preset swatches.

Import

import { ColorPicker } from "@puzzle-ui/ui";

Basic usage

Provide a visible label and optionally an initial defaultValue. Add preset colors with colors; a swatch can be a color string or an object with a value and optional accessible label.

390 × 300

Preset swatches

Use labeled swatch objects when the color value alone is not a useful accessible name. CSS color values such as variables can also be used as swatches; the picker returns the original value when a swatch is selected.

390 × 280

Controlled value and format

Use value with onChange when the parent owns the selected color; otherwise, use defaultValue for an uncontrolled picker. The callback receives the color string, or undefined when the color is cleared.

format determines the string produced when users adjust the picker controls. It supports "hex", "hexa", "rgb", and "hsl"; the default is "hexa". Selecting a swatch or applying a valid text value preserves that input string rather than converting it to the selected format.

import { useState } from "react";

const ColorSetting = () => {
  const [color, setColor] = useState<string | undefined>("#6750a4");

  return (
    <ColorPicker
      label="Accent color"
      value={color}
      onChange={setColor}
      format="hex"
    />
  );
};

Clearing and disabled state

The picker is clearable by default. Set clearable={false} to remove the clear action; customize its accessible name with clearLabel. Clearing calls onChange with undefined. Set disabled to prevent opening the picker.

position selects the preferred side of the trigger (top, right, bottom, or left); align selects start, center, or end alignment. The popup closes when users press Escape or click outside. Use open with onOpenChange to control popup visibility, or defaultOpen for its initial state.

API

PropTypeDefaultDescription
labelReactNode-Visible label and accessible name for the trigger and popup.
descriptionReactNode-Supporting text associated with the trigger.
valuestring | undefined-Controlled color value. Pass undefined to represent no selection.
defaultValuestring-Initial color for an uncontrolled picker.
onChange(value: string | undefined) => void-Called when a color is chosen, adjusted, applied, or cleared.
placeholderstring"Select color"Trigger text when no color is selected.
colorsreadonly ColorPickerSwatch[][]Preset strings or { value, label? } swatches.
format"hex" | "hexa" | "rgb" | "hsl""hexa"Output format for colors adjusted with the picker controls.
clearablebooleantrueWhether to show the clear-color action.
clearLabelstring"Clear color"Accessible name for the clear action.
disabledbooleanfalseDisables the trigger.
openboolean-Controlled popup visibility.
defaultOpenbooleanfalseInitial popup visibility when uncontrolled.
onOpenChange(open: boolean) => void-Called when popup visibility changes.
position"top" | "right" | "bottom" | "left""bottom"Preferred popup side.
align"start" | "center" | "end""start"Popup alignment relative to the trigger.
className, styleNative button props-Applied to the trigger button.
rootClassNamestring-Additional class for the root wrapper.
popoverClassNamestring-Additional class for the popup container.

Other native button attributes, including id, name, and aria-*, are forwarded to the trigger.

Accessibility

  • Always provide a label; without one, the trigger uses the generic accessible name “Choose color.”
  • Preset swatches are buttons with accessible names and pressed states. Give swatches a descriptive label when the raw color value is not meaningful.
  • The saturation/brightness area supports arrow keys; hold Shift to adjust in larger increments. Hue and opacity use native range inputs and support their standard keyboard controls.
  • Use Escape to close the popup; clicking outside also closes it.
;