Color Picker

HSL-native color picker that edits an HSL triplet directly via three sliders (H/S/L). Hex input is a display adapter; sliders are the source of truth so the value round-trips losslessly through the `@hex-core/tokens` triplet format.

Installation

pnpm
pnpm dlx @hex-core/cli add color-picker

Usage

tsx
import { Color Picker } from "@/components/ui/color-picker"

Edit a token live

Bind a state variable to a CSS custom property to preview a token edit in real time.

tsx
const [color, setColor] = React.useState("240 5.9% 10%");

return (
  <div style={{ "--primary": color } as React.CSSProperties}>
    <ColorPicker value={color} onChange={setColor} aria-label="Primary color" />
    <Button>Live preview</Button>
  </div>
);

Disabled

Prevent edits while a parent operation is in flight.

tsx
<ColorPicker value="240 5.9% 10%" onChange={() => {}} disabled />

API Reference

PropTypeDefaultDescription
valuerequired
stringCurrent color as an HSL triplet string (`"<H> <S>% <L>%"`, e.g. `"240 5.9% 10%"`). Match the format used by `@hex-core/tokens`.
onChangerequired
functionCalled with the next HSL triplet when the user drags a slider or commits a valid hex value. Not called for invalid hex input.
disabled
booleanfalseDisable interaction. Trigger renders dimmed; mouse and keyboard activation are blocked by the native `disabled` attribute.
aria-label
string"Pick color"Accessible name for the trigger button.
className
stringAdditional class names merged onto the trigger.

AI Guidance

When to use

Use whenever the user is editing a color that will round-trip through the `@hex-core/tokens` HSL triplet format — token editors, theme builders, branding panels, custom-color surfaces in design tools.

When not to use

Don't use for picking a color from a fixed palette — use a `Select` or `RadioGroup` of swatches. Don't use for image-based color sampling (eyedropper) — that's a separate primitive. Don't reach for ColorPicker when only a hex string matters: bind it directly to `<input type="color">` for the simplest cases.

Common mistakes

  • Treating the value as hex — the prop is an HSL triplet, not a hex string. Use `hexToHslTriplet` and `hslTripletToHex` from `@hex-core/components/lib/color` if you need to bridge.
  • Forgetting to wrap the value in `hsl(...)` when applying it as a CSS color: `style={{ color: \`hsl(${value})\` }}`.
  • Calling `onChange` synchronously inside a parent's render — the picker batches slider updates and that pattern can desync controlled state.

Accessibility

Each slider has a per-axis `aria-label` (Hue / Saturation / Lightness). The trigger button needs an explicit `aria-label` describing what color is being edited (e.g. `"Primary color"`) — the default `"Pick color"` is generic. The hex input is keyboard-accessible and round-trips with the sliders.

Related components

Token budget: 350