ColorPicker

A composable hex color picker with a 2D saturation/brightness area, a hue slider, and a hex input

Installation

pnpm add @wandercom/design-system-web

Usage

21 lines
import {
  ColorPicker,
  ColorPickerArea,
  ColorPickerContent,
  ColorPickerHueSlider,
  ColorPickerInput,
  ColorPickerTrigger,
} from '@wandercom/design-system-web/ui/color-picker';

export function Example() {
  return (
    <ColorPicker defaultValue="#6366f1" onValueChange={(value) => console.log(value)}>
      <ColorPickerTrigger />
      <ColorPickerContent>
        <ColorPickerArea />
        <ColorPickerHueSlider />
        <ColorPickerInput />
      </ColorPickerContent>
    </ColorPicker>
  );
}

Examples

The default example shows the popover picker, inline mode, and the disabled state.

Loading example...

Inline

Set inline on the root to render the picker directly in the page instead of inside a popover. ColorPickerContent becomes a plain container and no trigger is needed.

7 lines
<ColorPicker defaultValue="#22c55e" inline>
  <ColorPickerContent className="rounded-lg border border-overlay-primary">
    <ColorPickerArea />
    <ColorPickerHueSlider />
    <ColorPickerInput />
  </ColorPickerContent>
</ColorPicker>

Forms

Pass name to submit the current color (as a hex string) with the owning form via a hidden input. required, disabled, and readOnly are also supported.

10 lines
<form>
  <ColorPicker defaultValue="#6366f1" name="brandColor" required>
    <ColorPickerTrigger />
    <ColorPickerContent>
      <ColorPickerArea />
      <ColorPickerHueSlider />
      <ColorPickerInput />
    </ColorPickerContent>
  </ColorPicker>
</form>

Props

ColorPicker

value?:

string
Controlled color value as a hex or rgb(a) color string. Alpha is not supported and is stripped.

defaultValue?:

string
Initial color value when uncontrolled. Defaults to '#000000'.

onValueChange?:

(value: string) => void
Callback fired with the hex color string whenever the color changes.

open?:

boolean
The controlled open state of the popover.

defaultOpen?:

boolean
The initial open state of the popover when uncontrolled.

onOpenChange?:

(open: boolean) => void
Callback fired when the popover open state changes.

inline?:

boolean
Renders the picker inline instead of inside a popover.

name?:

string
Name submitted with the owning form via a hidden input.

disabled?:

boolean
Disables the picker and all of its subcomponents.

readOnly?:

boolean
Marks the hidden form input as read-only.

required?:

boolean
Marks the hidden form input as required.

dir?:

'ltr' | 'rtl'
Reading direction. Defaults to 'ltr'.

labels?:

ColorPickerLabels
Overrides for user-facing strings. Merged over the English defaults in DEFAULT_COLOR_PICKER_LABELS.

render?:

useRender.RenderProp
Replaces the root element with a custom element while preserving behavior.

ColorPickerContent

Accepts all PopoverContent props when rendered in a popover, including the positioning props (side, align, sideOffset, alignOffset, collisionPadding) and container to override the portal target.

render?:

useRender.RenderProp
Replaces the inline container element. Only applies when the root has inline set.

className?:

string
Additional CSS classes to apply.

ColorPickerInput

Renders the design system Input and accepts all of its props except value, onChange, and color.

className?:

string
Additional CSS classes to apply.

ColorPickerLabels

Every user-facing string is overridable through the labels prop on the root. Unspecified keys fall back to the English defaults in DEFAULT_COLOR_PICKER_LABELS.

hexInputLabel?:

string
Accessible label for the hex value input. Defaults to 'Hex color value'.

hueSliderLabel?:

string
Accessible label for the hue slider thumb. Defaults to 'Hue'.

noColorSelectedLabel?:

string
Accessible label for the swatch when no color is selected. Defaults to 'No color selected'.

hexPlaceholder?:

string
Placeholder for the hex value input. Defaults to '#000000'.

getSwatchLabel?:

(colorString: string) => string
Formats the accessible label announced for the current swatch color. Defaults to 'Current color: {colorString}'.

Accessibility

  • The 2D saturation/brightness area (ColorPickerArea) is pointer-only. The hue slider and the hex input together form the complete keyboard-accessible alternative — every color reachable by dragging the area can also be reached with arrow keys on the slider or by typing an exact hex value into the input.
  • The hue slider is built on the Base UI Slider primitive and supports Arrow keys, Home, End, Page Up, and Page Down.
  • ColorPickerSwatch renders with role="img" and announces the current color to screen readers (e.g. "Current color: #6366f1"), or "No color selected" when empty.
  • The hex input carries an accessible label describing its expected value.
  • All user-facing strings — the input label, placeholder, and the swatch announcement — are localizable via the labels prop.
  • The trigger, slider, and input all show :focus-visible focus indicators, and the popover follows the design system Popover focus behavior.

Limitations

  • The picker emits hex strings only (#rrggbb); alpha is not supported, and any alpha in an rgba() or 8-digit-hex input value is stripped.
  • The 2D area does not accept keyboard input; keep the slider and input present whenever the area is used.
ColorPicker