Components
Color picker

Color Picker

A component that allows users to select a color from a predefined set.

Usage

import { PipetteIcon } from 'lucide-react'
import { HStack, Stack } from 'styled-system/jsx'
import * as ColorPicker from '~/components/ui/color-picker'
import { IconButton } from '~/components/ui/icon-button'
import { Input } from '~/components/ui/input'
import { Text } from '~/components/ui/text'

export const Demo = (props: ColorPicker.RootProps) => {
  return (
    <ColorPicker.Root {...props}>
      {(api) => (
        <>
          <ColorPicker.Label>Color Picker</ColorPicker.Label>
          <ColorPicker.Control>
            <ColorPicker.ChannelInput channel="hex" asChild>
              <Input />
            </ColorPicker.ChannelInput>
            <ColorPicker.Trigger asChild>
              <IconButton variant="outline">
                <ColorPicker.Swatch value={api.value} />
              </IconButton>
            </ColorPicker.Trigger>
          </ColorPicker.Control>
          <ColorPicker.Positioner>
            <ColorPicker.Content>
              <Stack gap="3">
                <ColorPicker.Area>
                  <ColorPicker.AreaBackground />
                  <ColorPicker.AreaThumb />
                </ColorPicker.Area>
                <HStack gap="3">
                  <ColorPicker.EyeDropperTrigger asChild>
                    <IconButton size="xs" variant="outline" aria-label="Pick a color">
                      <PipetteIcon />
                    </IconButton>
                  </ColorPicker.EyeDropperTrigger>
                  <Stack gap="2" flex="1">
                    <ColorPicker.ChannelSlider channel="hue">
                      <ColorPicker.ChannelSliderTrack />
                      <ColorPicker.ChannelSliderThumb />
                    </ColorPicker.ChannelSlider>
                    <ColorPicker.ChannelSlider channel="alpha">
                      <ColorPicker.TransparencyGrid size="8px" />
                      <ColorPicker.ChannelSliderTrack />
                      <ColorPicker.ChannelSliderThumb />
                    </ColorPicker.ChannelSlider>
                  </Stack>
                </HStack>
                <HStack>
                  <ColorPicker.ChannelInput channel="hex" asChild>
                    <Input size="2xs" />
                  </ColorPicker.ChannelInput>
                  <ColorPicker.ChannelInput channel="alpha" asChild>
                    <Input size="2xs" />
                  </ColorPicker.ChannelInput>
                </HStack>
                <Stack gap="1.5">
                  <Text size="xs" fontWeight="medium" color="fg.default">
                    Saved Colors
                  </Text>
                  <ColorPicker.SwatchGroup>
                    {presets.map((color, id) => (
                      <ColorPicker.SwatchTrigger key={id} value={color}>
                        <ColorPicker.Swatch value={color} />
                      </ColorPicker.SwatchTrigger>
                    ))}
                  </ColorPicker.SwatchGroup>
                </Stack>
              </Stack>
            </ColorPicker.Content>
          </ColorPicker.Positioner>
        </>
      )}
    </ColorPicker.Root>
  )
}

const presets = [
  'hsl(10, 81%, 59%)',
  'hsl(60, 81%, 59%)',
  'hsl(100, 81%, 59%)',
  'hsl(175, 81%, 59%)',
  'hsl(190, 81%, 59%)',
  'hsl(205, 81%, 59%)',
  'hsl(220, 81%, 59%)',
  'hsl(250, 81%, 59%)',
  'hsl(280, 81%, 59%)',
  'hsl(350, 81%, 59%)',
]

Installation

1

Add Component

Insert code snippet into your project. Update import paths as needed.

import { ColorPicker } from '@ark-ui/react/color-picker'
import type { ComponentProps } from 'react'
import { styled } from 'styled-system/jsx'
import { colorPicker } from 'styled-system/recipes'
import { createStyleContext } from '~/lib/create-style-context'

const { withProvider, withContext } = createStyleContext(colorPicker)

export const Root = withProvider(styled(ColorPicker.Root), 'root')
export const Area = withContext(styled(ColorPicker.Area), 'area')
export const AreaBackground = withContext(styled(ColorPicker.AreaBackground), 'areaBackground')
export const AreaThumb = withContext(styled(ColorPicker.AreaThumb), 'areaThumb')
export const ChannelInput = withContext(styled(ColorPicker.ChannelInput), 'channelInput')
export const ChannelSlider = withContext(styled(ColorPicker.ChannelSlider), 'channelSlider')
export const ChannelSliderThumb = withContext(
  styled(ColorPicker.ChannelSliderThumb),
  'channelSliderThumb',
)
export const ChannelSliderTrack = withContext(
  styled(ColorPicker.ChannelSliderTrack),
  'channelSliderTrack',
)
export const Content = withContext(styled(ColorPicker.Content), 'content')
export const Control = withContext(styled(ColorPicker.Control), 'control')
export const EyeDropperTrigger = withContext(
  styled(ColorPicker.EyeDropperTrigger),
  'eyeDropperTrigger',
)
export const FormatSelect = withContext(styled(ColorPicker.FormatSelect), 'formatSelect')
export const FormatTrigger = withContext(styled(ColorPicker.FormatTrigger), 'formatTrigger')
export const Label = withContext(styled(ColorPicker.Label), 'label')
export const Positioner = withContext(styled(ColorPicker.Positioner), 'positioner')
export const Swatch = withContext(styled(ColorPicker.Swatch), 'swatch')
export const SwatchGroup = withContext(styled(ColorPicker.SwatchGroup), 'swatchGroup')
export const SwatchIndicator = withContext(styled(ColorPicker.SwatchIndicator), 'swatchIndicator')
export const SwatchTrigger = withContext(styled(ColorPicker.SwatchTrigger), 'swatchTrigger')
export const TransparencyGrid = withContext(
  styled(ColorPicker.TransparencyGrid),
  'transparencyGrid',
)
export const Trigger = withContext(styled(ColorPicker.Trigger), 'trigger')
export const ValueText = withContext(styled(ColorPicker.ValueText), 'valueText')
export const View = withContext(styled(ColorPicker.View), 'view')

export interface RootProps extends ComponentProps<typeof Root> {}
export interface AreaProps extends ComponentProps<typeof Area> {}
export interface AreaBackgroundProps extends ComponentProps<typeof AreaBackground> {}
export interface AreaThumbProps extends ComponentProps<typeof AreaThumb> {}
export interface ChannelInputProps extends ComponentProps<typeof ChannelInput> {}
export interface ChannelSliderProps extends ComponentProps<typeof ChannelSlider> {}
export interface ChannelSliderThumbProps extends ComponentProps<typeof ChannelSliderThumb> {}
export interface ChannelSliderTrackProps extends ComponentProps<typeof ChannelSliderTrack> {}
export interface ContentProps extends ComponentProps<typeof Content> {}
export interface ControlProps extends ComponentProps<typeof Control> {}
export interface EyeDropperTriggerProps extends ComponentProps<typeof EyeDropperTrigger> {}
export interface FormatSelectProps extends ComponentProps<typeof FormatSelect> {}
export interface FormatTriggerProps extends ComponentProps<typeof FormatTrigger> {}
export interface LabelProps extends ComponentProps<typeof Label> {}
export interface PositionerProps extends ComponentProps<typeof Positioner> {}
export interface SwatchProps extends ComponentProps<typeof Swatch> {}
export interface SwatchGroupProps extends ComponentProps<typeof SwatchGroup> {}
export interface SwatchIndicatorProps extends ComponentProps<typeof SwatchIndicator> {}
export interface SwatchTriggerProps extends ComponentProps<typeof SwatchTrigger> {}
export interface TransparencyGridProps extends ComponentProps<typeof TransparencyGrid> {}
export interface TriggerProps extends ComponentProps<typeof Trigger> {}
export interface ValueTextProps extends ComponentProps<typeof ValueText> {}
export interface ViewProps extends ComponentProps<typeof View> {}
2

Add Recipe

This step is necessary only if you do not use any of the Park UI plugins.

import { colorPickerAnatomy } from '@ark-ui/anatomy'
import { defineSlotRecipe } from '@pandacss/dev'

export const colorPicker = defineSlotRecipe({
  className: 'colorPicker',
  slots: colorPickerAnatomy.keys(),
  base: {
    root: {
      display: 'flex',
      flexDirection: 'column',
      gap: '1.5',
    },
    label: {
      color: 'fg.default',
      fontWeight: 'medium',
      textStyle: 'sm',
    },
    control: {
      display: 'flex',
      flexDirection: 'row',
      gap: '2',
    },
    content: {
      background: 'bg.default',
      borderRadius: 'l3',
      boxShadow: 'lg',
      display: 'flex',
      flexDirection: 'column',
      maxWidth: 'sm',
      p: '4',
      zIndex: 'dropdown',
      _open: {
        animation: 'fadeIn 0.25s ease-out',
      },
      _closed: {
        animation: 'fadeOut 0.2s ease-out',
      },
      _hidden: {
        display: 'none',
      },
    },
    area: {
      height: '36',
      borderRadius: 'l2',
      overflow: 'hidden',
    },
    areaThumb: {
      borderRadius: 'full',
      height: '2.5',
      width: '2.5',
      boxShadow: 'white 0px 0px 0px 2px, black 0px 0px 2px 1px',
      outline: 'none',
    },
    areaBackground: {
      height: 'full',
    },
    channelSlider: {
      borderRadius: 'l2',
    },
    channelSliderTrack: {
      height: '3',
      borderRadius: 'l2',
    },
    swatchGroup: {
      display: 'grid',
      gridTemplateColumns: 'repeat(7, 1fr)',
      gap: '2',
      background: 'bg.default',
    },
    swatch: {
      height: '6',
      width: '6',
      borderRadius: 'l2',
      boxShadow:
        '0 0 0 1px var(--colors-border-emphasized), 0 0 0 2px var(--colors-bg-default) inset',
    },
    channelSliderThumb: {
      borderRadius: 'full',
      height: '2.5',
      width: '2.5',
      boxShadow: 'white 0px 0px 0px 2px, black 0px 0px 2px 1px',
      transform: 'translate(-50%, -50%)',
      outline: 'none',
    },
    transparencyGrid: {
      borderRadius: 'l2',
    },
  },
})

On this page