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.
Single token
262 83% 58%Additional tokens
199 89% 48%0 84% 60%Disabled — locked while a parent operation is in flight
240 5.9% 10%Installation
pnpm dlx @hex-core/cli add color-pickerDisabled
Prevent edits while a parent operation is in flight.
<ColorPicker value="240 5.9% 10%" onChange={() => {}} disabled />With hex input
Pair the picker with a sibling hex Input. The triplet stays the source of truth; the hex field is derived for display via hslTripletToHex and parsed back via hexToHslTriplet. Both helpers are re-exported from the @hex-core/components barrel.
import { useState } from "react";
import { ColorPicker } from "@/components/ui/color-picker";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { hexToHslTriplet, hslTripletToHex } from "@hex-core/components";
export function BrandColorField() {
const [triplet, setTriplet] = useState("262 83% 58%");
const hex = hslTripletToHex(triplet);
return (
<div className="flex items-end gap-3">
<div className="space-y-1">
<Label>Brand color</Label>
<ColorPicker value={triplet} onChange={setTriplet} aria-label="Brand color" />
</div>
<div className="space-y-1">
<Label htmlFor="brand-hex">Hex</Label>
<Input
id="brand-hex"
value={hex}
onChange={(event) => {
const next = hexToHslTriplet(event.target.value);
if (next) setTriplet(next);
}}
className="w-28 font-mono text-sm uppercase"
spellCheck={false}
/>
</div>
</div>
);
}API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
valuerequired | string | — | Current color as an HSL triplet string (`"<H> <S>% <L>%"`, e.g. `"240 5.9% 10%"`). Match the format used by `@hex-core/tokens`. |
onChangerequired | function | — | Called with the next HSL triplet when the user drags a slider or commits a valid hex value. Not called for invalid hex input. |
disabled | boolean | false | Disable 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 | string | — | Additional 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` 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.
Token budget: 350
Verified against @hex-core/components@1.12.0