VayuUI

ColorPicker

A color selection component with presets, format conversion, and screen picker support.

Installation

npx vayu-ui init #one time only
npx vayu-ui add colorpicker
npx vayu-ui add -t colorpicker #with test case needs

Usage

Choose a primary color for your brand.

Select a color for your theme.

<ColorPicker value={color} onChange={setColor}>
  <ColorPicker.Label>Brand Color</ColorPicker.Label>
  <ColorPicker.Description>
    Choose a primary color for your brand.
  </ColorPicker.Description>
  <div className="flex items-center gap-2">
    <ColorPicker.Trigger />
    <ColorPicker.Input />
    <ColorPicker.CopyButton />
  </div>
  <ColorPicker.Content>
    <div className="flex flex-col gap-4">
      <ColorPicker.Palette />
      <ColorPicker.Eyedropper />
      <ColorPicker.Presets />
    </div>
  </ColorPicker.Content>
</ColorPicker>

<ColorPicker value={color} onChange={setColor} format="rgb">
  <ColorPicker.Label>Background Color</ColorPicker.Label>
  <div className="flex items-center gap-2">
    <ColorPicker.Trigger size="sm" />
    <ColorPicker.Input />
    <ColorPicker.CopyButton />
  </div>
  <ColorPicker.Content>
    <div className="flex flex-col gap-4">
      <ColorPicker.Palette />
      <ColorPicker.Presets columns={6} />
    </div>
  </ColorPicker.Content>
</ColorPicker>

<ColorPicker validationState="error">
  <ColorPicker.Label>Theme Color</ColorPicker.Label>
  <ColorPicker.Description>
    Select a color for your theme.
  </ColorPicker.Description>
  <div className="flex items-center gap-2">
    <ColorPicker.Trigger />
    <ColorPicker.Input />
  </div>
  <ColorPicker.Error>
    This color does not meet contrast requirements.
  </ColorPicker.Error>
</ColorPicker>

<ColorPicker.Swatches
  label="Quick Pick"
  colors={["#ef4444", "#f97316", "#eab308", "#22c55e", "#3b82f6", "#8b5cf6", "#ec4899", "#171717"]}
  value={swatchColor}
  onChange={setSwatchColor}
  size="lg"
  columns={4}
/>

<ColorPicker defaultValue="#6366f1" disabled>
  <ColorPicker.Label>Locked Color</ColorPicker.Label>
  <div className="flex items-center gap-2">
    <ColorPicker.Trigger />
    <ColorPicker.Input />
  </div>
</ColorPicker>

Anatomy

ColorPicker
├── ColorPicker.Label              -- Accessible label linked to the input
├── ColorPicker.Description        -- Helper text below the label
├── ColorPicker.Error              -- Error message (renders only in error state)
├── ColorPicker.Trigger            -- Color swatch button that toggles the dropdown
├── ColorPicker.Input              -- Text input for hex/rgb/hsl values
├── ColorPicker.CopyButton         -- Copies current color to clipboard
└── ColorPicker.Content            -- Dropdown panel with positioning
    ├── ColorPicker.Palette        -- Native browser color picker
    ├── ColorPicker.Eyedropper     -- Screen color picker (Chrome/Edge only)
    └── ColorPicker.Presets        -- Grid of preset color swatches

ColorPicker.Swatches               -- Standalone swatch grid (no context required)

Accessibility

Keyboard support

KeyContextBehavior
EnterTriggerOpens the dropdown
SpaceTriggerOpens the dropdown
EscapeContent (open)Closes dropdown, returns focus
TabAnyMoves focus to next element

ARIA attributes

ComponentAttributeValue
Triggeraria-expandedtrue / false
Triggeraria-controlsDropdown ID
Triggeraria-labelledbyLabel ID
Triggeraria-haspopup"dialog"
Inputaria-labelledbyLabel ID
Inputaria-describedbyDescription + error IDs
Inputaria-invalidtrue when in error state
Contentrole"dialog"
Contentaria-modalfalse
Errorrole"alert"
Erroraria-live"polite"
Presets / Swatchesrole"listbox" / "option"
Presets / Swatchesaria-selectedtrue on active swatch
CopyButtonaria-labelDynamic: "Copy color" / "Copied!"
Eyedropperaria-busytrue while picking

Focus behavior

  • Trigger receives focus via Tab
  • Content auto-focuses on open via requestAnimationFrame
  • Escape closes dropdown and returns focus to the trigger
  • Click outside closes dropdown and maintains focus
  • All interactive elements have visible focus-visible ring

Screen reader behavior

  • The trigger announces: "Select color. Current color: {hex value}"
  • The label is associated with the input via htmlFor and aria-labelledby
  • Description text is linked to the input via aria-describedby
  • In error state, the error message is announced via aria-live="polite" and role="alert"
  • Preset swatches are announced as a listbox with options: "Select color {hex}"
  • The selected swatch is indicated by aria-selected="true"
  • The copy button announces "Copied!" after a successful clipboard copy
  • The eyedropper announces its label and sets aria-busy during active picking

Component Folder Structure

ColorPicker/
├── ColorPicker.tsx
├── ColorPickerContent.tsx
├── ColorPickerCopyButton.tsx
├── ColorPickerDescription.tsx
├── ColorPickerError.tsx
├── ColorPickerEyeDropper.tsx
├── ColorPickerInput.tsx
├── ColorPickerLabel.tsx
├── ColorPickerPalette.tsx
├── ColorPickerPresets.tsx
├── ColorPickerSwatches.tsx
├── ColorPickerTrigger.tsx
├── hooks.ts
├── index.ts
├── types.ts
├── utils.ts
└── README.md

Props

ColorPicker

PropTypeDefaultDescription
valuestringControlled color value (hex)
defaultValuestring"#3b82f6"Uncontrolled default color
onChange(color: string) => voidCallback when color changes
format"hex" | "rgb" | "hsl""hex"Display format in the text input
presetsstring[]22 defaultsCustom preset color array
disabledbooleanfalseDisable all interactions
validationState"default" | "error" | "warning" | "success""default"Input validation state
defaultOpenbooleanfalseInitial dropdown open state
openbooleanControlled dropdown state
onOpenChange(open: boolean) => voidCallback when dropdown toggles

ColorPicker.Label

PropTypeDefaultDescription
childrenReactNodeLabel text
optionalbooleanfalseShows "(optional)" text

ColorPicker.Description

PropTypeDefaultDescription
childrenReactNodeHelper text

ColorPicker.Error

PropTypeDefaultDescription
childrenReactNodeError message (only renders in error state)

ColorPicker.Trigger

PropTypeDefaultDescription
size"sm" | "md" | "lg""md"Swatch size (32/48/64 px)

ColorPicker.Input

PropTypeDefaultDescription
placeholderstring"#000000"Input placeholder

ColorPicker.CopyButton

PropTypeDefaultDescription
copiedTextstring"Copied!"Text shown after copying

ColorPicker.Content

PropTypeDefaultDescription
side"top" | "bottom""bottom"Preferred vertical position
align"start" | "center" | "end""start"Horizontal alignment to trigger
sideOffsetnumber8Distance from trigger (px)

ColorPicker.Palette

PropTypeDefaultDescription
labelstring"Pick a color"Accessible label text

ColorPicker.Eyedropper

PropTypeDefaultDescription
labelstring"Pick from screen"Button text
unsupportedTextstring"Eyedropper not supported in this browser"Fallback message

ColorPicker.Presets

PropTypeDefaultDescription
labelstring"Preset Colors"Section label
colorsstring[]Context presetsOverride preset colors
columnsnumber8Grid column count

ColorPicker.Swatches

PropTypeDefaultDescription
colorsstring[]Array of hex colors
valuestringCurrently selected color
onChange(color) => voidCallback on swatch click
size"sm" | "md" | "lg""md"Swatch size (24/32/40 px)
columnsnumber8Grid column count
labelstringSection label
disabledbooleanfalseDisable all swatches

On this page