Skip to content
Lenso UI

ColorPicker

A composable color picker that synchronizes color value between multiple color components

Usage

import {  ColorPicker,  ColorArea,  ColorSlider,  ColorSwatch,  ColorField,  ColorSwatchPicker,} from '@lenso/ui';
"use client";
import { ColorArea, ColorPicker, ColorSlider, ColorSwatch } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";
const styles = stylex.create({  area: { maxWidth: "100%" },  slider: { gap: 4, paddingInline: 4 },  output: { color: "var(--muted)" },});export function Basic() {  return (    <ColorPicker defaultValue="#0485F7">      <ColorPicker.Trigger>        <ColorSwatch size="lg" />        <span>Pick a color</span>      </ColorPicker.Trigger>      <ColorPicker.Popover>        <ColorArea          aria-label="Color area"          xstyle={styles.area}          colorSpace="hsb"          xChannel="saturation"          yChannel="brightness"        >          <ColorArea.Thumb />        </ColorArea>        <ColorSlider aria-label="Hue" channel="hue" xstyle={styles.slider} colorSpace="hsb">          <span>Hue</span>          <ColorSlider.Output xstyle={styles.output} />          <ColorSlider.Track>            <ColorSlider.Thumb />          </ColorSlider.Track>        </ColorSlider>      </ColorPicker.Popover>    </ColorPicker>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

Anatomy

The ColorPicker is a composable component that combines multiple color components:

import { ColorPicker, ColorArea, ColorSlider, ColorSwatch, Label } from '@lenso/ui';
export default () => (  <ColorPicker defaultValue="#0485F7">    <ColorPicker.Trigger>      <ColorSwatch />      <Label>Pick a color</Label>    </ColorPicker.Trigger>    <ColorPicker.Popover>      <ColorArea colorSpace="hsb" xChannel="saturation" yChannel="brightness">        <ColorArea.Thumb />      </ColorArea>      <ColorSlider channel="hue" colorSpace="hsb">        <ColorSlider.Track>          <ColorSlider.Thumb />        </ColorSlider.Track>      </ColorSlider>    </ColorPicker.Popover>  </ColorPicker>);

Examples

Controlled

"use client";/** Adapted from HeroUI v3.2.6. Apache-2.0. Gravity Shuffle replaces the source Iconify reference to the same icon. */import {  Button,  ColorArea,  ColorField,  ColorPicker,  ColorSlider,  ColorSwatch,  ColorSwatchPicker,  parseColor,} from "@lenso/ui";import { Shuffle } from "@gravity-ui/icons";import { useState } from "react";import * as stylex from "@stylexjs/stylex";import { styles } from "./source.stylex";const presets = [  "#ef4444",  "#f97316",  "#eab308",  "#22c55e",  "#06b6d4",  "#3b82f6",  "#8b5cf6",  "#ec4899",  "#f43f5e",];export function Controlled() {  const [color, setColor] = useState(parseColor("#325578"));  function shuffleColor() {    const hue = Math.floor(Math.random() * 360);    const saturation = 50 + Math.floor(Math.random() * 50);    const lightness = 40 + Math.floor(Math.random() * 30);    setColor(parseColor(`hsl(${hue}, ${saturation}%, ${lightness}%)`));  }  return (    <div {...stylex.props(styles.column)}>      <ColorPicker value={color} onChange={setColor}>        <ColorPicker.Trigger>          <ColorSwatch size="lg" />          <span>Pick a color</span>        </ColorPicker.Trigger>        <ColorPicker.Popover xstyle={styles.popover}>          <ColorSwatchPicker aria-label="Color presets" xstyle={styles.presets} size="xs">            {presets.map((preset) => (              <ColorSwatchPicker.Item key={preset} color={preset}>                <ColorSwatchPicker.Swatch />              </ColorSwatchPicker.Item>            ))}          </ColorSwatchPicker>          <ColorArea            aria-label="Color area"            xstyle={styles.area}            colorSpace="hsb"            xChannel="saturation"            yChannel="brightness"          >            <ColorArea.Thumb />          </ColorArea>          <div {...stylex.props(styles.hueRow)}>            <ColorSlider              aria-label="Hue slider"              channel="hue"              xstyle={styles.flex1}              colorSpace="hsb"            >              <ColorSlider.Track>                <ColorSlider.Thumb />              </ColorSlider.Track>            </ColorSlider>            <Button              isIconOnly              aria-label="Shuffle color"              size="sm"              variant="tertiary"              onClick={shuffleColor}            >              <Shuffle {...stylex.props(styles.icon)} />            </Button>          </div>          <ColorField aria-label="Color field">            <ColorField.Group variant="secondary">              <ColorField.Prefix>                <ColorSwatch size="xs" />              </ColorField.Prefix>              <ColorField.Input />            </ColorField.Group>          </ColorField>        </ColorPicker.Popover>      </ColorPicker>      <p {...stylex.props(styles.selected)}>        Selected: <span {...stylex.props(styles.medium)}>{color.toString("hex")}</span>      </p>    </div>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

With Swatches

"use client";/** Adapted from HeroUI v3.2.6. Apache-2.0. */import { ColorArea, ColorPicker, ColorSlider, ColorSwatch, ColorSwatchPicker } from "@lenso/ui";import { styles } from "./source.stylex";const presets = [  "#ef4444",  "#f97316",  "#eab308",  "#22c55e",  "#06b6d4",  "#3b82f6",  "#8b5cf6",  "#ec4899",  "#f43f5e",];export function WithSwatches() {  return (    <ColorPicker defaultValue="#F43F5E">      <ColorPicker.Trigger>        <ColorSwatch size="lg" />        <span>Brand Color</span>      </ColorPicker.Trigger>      <ColorPicker.Popover>        <ColorArea          aria-label="Color area"          xstyle={styles.area}          colorSpace="hsb"          xChannel="saturation"          yChannel="brightness"        >          <ColorArea.Thumb />        </ColorArea>        <ColorSlider aria-label="Hue slider" channel="hue" xstyle={styles.slider} colorSpace="hsb">          <ColorSlider.Label>Hue</ColorSlider.Label>          <ColorSlider.Output xstyle={styles.output} />          <ColorSlider.Track>            <ColorSlider.Thumb />          </ColorSlider.Track>        </ColorSlider>        <ColorSwatchPicker aria-label="Color presets" xstyle={styles.swatches} size="xs">          {presets.map((preset) => (            <ColorSwatchPicker.Item key={preset} color={preset}>              <ColorSwatchPicker.Swatch />            </ColorSwatchPicker.Item>          ))}        </ColorSwatchPicker>      </ColorPicker.Popover>    </ColorPicker>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

With Fields

Use ColorField to allow users to edit individual color channel values with a Select to switch between color spaces.

"use client";/** Adapted from HeroUI v3.2.6. Apache-2.0. */import { ColorArea, ColorField, ColorPicker, ColorSlider, ColorSwatch } from "@lenso/ui";import { useState } from "react";import * as stylex from "@stylexjs/stylex";import { styles } from "./source.stylex";import { ColorDemoSelect } from "./source";type Space = "hsb" | "hsl" | "rgb";const channels = {  hsb: ["hue", "saturation", "brightness"],  hsl: ["hue", "saturation", "lightness"],  rgb: ["red", "green", "blue"],} as const;export function WithFields() {  const [colorSpace, setColorSpace] = useState<Space>("hsl");  return (    <ColorPicker defaultValue="hsla(220, 90%, 50%, 0.8)">      <ColorPicker.Trigger>        <ColorSwatch size="lg" />        <span>Pick a color</span>      </ColorPicker.Trigger>      <ColorPicker.Popover xstyle={styles.narrowPopover}>        <ColorArea          aria-label="Color area"          xstyle={styles.area}          colorSpace="hsb"          xChannel="saturation"          yChannel="brightness"        >          <ColorArea.Thumb />        </ColorArea>        <ColorSlider channel="hue" xstyle={styles.slider} colorSpace="hsb">          <ColorSlider.Label>Hue</ColorSlider.Label>          <ColorSlider.Output xstyle={styles.output} />          <ColorSlider.Track>            <ColorSlider.Thumb />          </ColorSlider.Track>        </ColorSlider>        <ColorDemoSelect          label="Color space"          value={colorSpace}          options={["hsb", "hsl", "rgb"]}          onChange={setColorSpace}          uppercase        />        <div {...stylex.props(styles.channelGrid)}>          {channels[colorSpace].map((channel) => (            <ColorField              key={channel}              aria-label={channel}              channel={channel}              colorSpace={colorSpace}            >              <ColorField.Group variant="secondary">                <ColorField.Input />              </ColorField.Group>            </ColorField>          ))}        </div>      </ColorPicker.Popover>    </ColorPicker>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

With Sliders

Use multiple ColorSlider components to adjust each channel of a color value.

"use client";/** Adapted from HeroUI v3.2.6. Apache-2.0. */import { ColorPicker, ColorSlider, ColorSwatch } from "@lenso/ui";import { useState } from "react";import * as stylex from "@stylexjs/stylex";import { styles } from "./source.stylex";import { ColorDemoSelect } from "./source";type Space = "hsb" | "hsl" | "rgb";const channelProps = {  hsb: [    { colorSpace: "hsb", channel: "hue" },    { colorSpace: "hsb", channel: "saturation" },    { colorSpace: "hsb", channel: "brightness" },    { colorSpace: "hsb", channel: "alpha" },  ],  hsl: [    { colorSpace: "hsl", channel: "hue" },    { colorSpace: "hsl", channel: "saturation" },    { colorSpace: "hsl", channel: "lightness" },    { colorSpace: "hsl", channel: "alpha" },  ],  rgb: [    { colorSpace: "rgb", channel: "red" },    { colorSpace: "rgb", channel: "green" },    { colorSpace: "rgb", channel: "blue" },    { colorSpace: "rgb", channel: "alpha" },  ],} as const;export function WithSliders() {  const [colorSpace, setColorSpace] = useState<Space>("hsl");  return (    <ColorPicker defaultValue="hsl(219, 58%, 93%)">      <ColorPicker.Trigger>        <ColorSwatch size="lg" />        <span>Pick a color</span>      </ColorPicker.Trigger>      <ColorPicker.Popover xstyle={styles.slidersPopover}>        <ColorDemoSelect          label="Color space"          value={colorSpace}          options={["hsb", "hsl", "rgb"]}          onChange={setColorSpace}          uppercase        />        <div {...stylex.props(styles.column2)}>          {channelProps[colorSpace].map((props) => (            <ColorSlider              key={props.channel}              {...props}              aria-label={props.channel}              xstyle={styles.slider}            >              <ColorSlider.Label xstyle={styles.capitalize}>{props.channel}</ColorSlider.Label>              <ColorSlider.Output xstyle={styles.output} />              <ColorSlider.Track>                <ColorSlider.Thumb />              </ColorSlider.Track>            </ColorSlider>          ))}        </div>      </ColorPicker.Popover>    </ColorPicker>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

Customization

Tailwind CSS

"use client";/** Adapted from HeroUI v3.2.6. Apache-2.0. */import { ColorArea, ColorPicker, ColorSlider, ColorSwatch } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";import { styles } from "./source.stylex";export function CustomStyles() {  return (    <ColorPicker defaultValue="#0485F7">      <ColorPicker.Trigger xstyle={styles.trigger}>        <ColorSwatch size="lg" />        <span {...stylex.props(styles.label)}>Theme color</span>      </ColorPicker.Trigger>      <ColorPicker.Popover xstyle={styles.surface}>        <ColorArea          aria-label="Color area"          xstyle={styles.area}          colorSpace="hsb"          xChannel="saturation"          yChannel="brightness"        >          <ColorArea.Thumb />        </ColorArea>        <ColorSlider channel="hue" xstyle={styles.slider} colorSpace="hsb">          <ColorSlider.Label>Hue</ColorSlider.Label>          <ColorSlider.Output xstyle={styles.output} />          <ColorSlider.Track>            <ColorSlider.Thumb />          </ColorSlider.Track>        </ColorSlider>      </ColorPicker.Popover>    </ColorPicker>  );}

Local adaptation source above. Derived from HeroUI v3.2.6 source.

Global CSS

To customize the ColorPicker component classes, you can use the @layer components directive. Learn more.

@layer components {  .color-picker {    @apply inline-flex;  }
  .color-picker__trigger {    @apply inline-flex items-center gap-4 rounded-lg;  }
  .color-picker__popover {    @apply p-4 rounded-xl;  }}

Styling Reference

HeroUI follows the BEM methodology to ensure component variants and states are reusable and easy to customize.

CSS Classes

The ColorPicker component uses these CSS classes (View source styles):

Base Classes [!toc]

  • .color-picker - Base container
  • .color-picker__trigger - Trigger button
  • .color-picker__popover - Popover container

Interactive States

The component supports both CSS pseudo-classes and data attributes for flexibility:

  • Focus: :focus-visible or [data-focus-visible="true"]
  • Disabled: :disabled or [data-disabled="true"]

API Reference

ColorPicker

Inherits from React Aria ColorPicker.

PropTypeDefaultDescription
valuestring | Color-The current color value (controlled)
defaultValuestring | Color-The default color value (uncontrolled)
onChange(color: Color) => void-Handler called when the color changes
childrenReact.ReactNode-Content of the color picker (Trigger, Popover, etc.)
classNamestring-Additional CSS classes

ColorPicker.Trigger

PropTypeDefaultDescription
childrenReact.ReactNode | ((renderProps) => React.ReactNode)-Trigger content or render prop
classNamestring-Additional CSS classes

ColorPicker.Popover

PropTypeDefaultDescription
placementPlacement"bottom left"Placement of the popover
childrenReact.ReactNode-Popover content
classNamestring-Additional CSS classes

Color

Represents a color value. See React Aria Color for full API.

MethodDescription
toString(format)Converts the color to a string in the given format (hex, rgb, hsl, hsb, css)
toFormat(format)Converts the color to the given format and returns a new Color object
getChannelValue(channel)Returns the numeric value for a given channel
withChannelValue(channel, value)Sets the numeric value for a channel and returns a new Color

parseColor

import { parseColor } from 'react-aria-components';
// Parse from stringconst color = parseColor('#ff0000');const hslColor = parseColor('hsl(0, 100%, 50%)');