Color Picker
Choose a color with sliders, swatches, hex or OKLCH values, or an eyedropper.
import { ColorPicker, Swatch } from '@youj/react'
Installation & usage
npm i @youj/reactFor an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.
Use it when people choose any color: a brand accent, a label, a chart series, a theme. The trigger shows the swatch and hex in the width of a text field, and the popover holds every way in: drag in the area, slide the hue, type a hex or OKLCH value, pick a swatch, or sample the screen with the browser's eyedropper. When the choice is one of a few named colors, a Toggle Group or a Radio Group of swatches is quicker.
import { useState } from 'react'
import { ColorPicker, Field, FieldLabel } from '@youj/react'
export function BrandColor() {
const [color, setColor] = useState('#3554fc')
return (
<Field>
<FieldLabel>Brand color</FieldLabel>
<ColorPicker value={color} onValueChange={setColor} aria-label="Brand color" />
</Field>
)
}value takes a hex or OKLCH string. The color is held as hue, saturation and brightness inside, so dragging to gray or black and back keeps the hue you had.
Description
A brand color field with a live preview of a button and a link in the chosen color.
- The area sets saturation across and brightness up and down. Click or drag anywhere in it. The thumb follows the pointer exactly, and glides to a new place when the color changes from a swatch or a field.
- The hue is a Slider on a spectrum track, from 0 to 359 degrees.
- The eyedropper samples any pixel on screen, in browsers that have one. Elsewhere the button isn't shown.
- The fields take hex and OKLCH. They commit on Enter or when they lose focus. Something they can't read is marked invalid, and put back on blur.
- Swatches default to the theme presets' accents, and the selected one is ringed.
A picker in OKLCH with five label swatches of your own, and the value it holds shown under it.
format="oklch" makes onValueChange report OKLCH strings, which suit a theme built on perceptual lightness. swatches replaces the presets with your own { value, label } pairs, and swatches={[]} removes the row.
Code
color-picker-demo.tsx, the Demo story. The component itself comes from the package.
Reference
Propsvalue, defaultValue, onValueChange… (10)
Props
| Prop | Type | |
|---|---|---|
value | stringA hex or OKLCH color string. | – |
defaultValue | stringdefault palettes[0].accent.light | palettes[0].accent.light |
onValueChange | (value: string) => void | – |
format | 'hex' | 'oklch'What onValueChange reports. default 'hex' | 'hex' |
swatches | Swatch[]Default: the theme presets' accents. Pass [] for none. | – |
disabled | boolean | – |
aria-label | stringNames the trigger. default 'Color' | 'Color' |
id | string | – |
className | string | – |
presentation | OverlayPresentation'auto''sheet''popover'
default 'auto' | 'auto' |
swatchOnly | booleanThe trigger is the swatch alone, for a color that sits inside another control (ColorOpacityField). The hex is still in its name. | – |
presetSwatches, the theme presets' accents, is exported for building on.
TouchOn a touch device the picker opens in a bottom sheet
Touch
On a touch device the picker opens in a bottom sheet. The area is taller and its thumb larger, swatches are 44px targets, and the fields use 16px text so the page doesn't zoom. Dragging in the area or along the hue slider changes the color and never pulls the sheet down; pull from the handle or the space around the controls to close it, or tap outside it.
presentation chooses: auto (the default) is a popover under a mouse or trackpad and a sheet on touch, sheet always uses the sheet, and popover keeps the popover on touch too.
MotionPopover opens, area thumb, swatch ring… · reduced motion
Motion
| Moment | Tokens | ||
|---|---|---|---|
Popover opensscales from 96% and fades in from the trigger (yj-pop) | duration.fastease.enter140ms · cubic-bezier(0.16, 1, 0.3, 1) | 140mscubic-bezier(0.16, 1, 0.3, 1) | |
| Area thumbglides when the color changes from a swatch or a field; follows a drag 1:1 | duration.fastease.standard140ms · cubic-bezier(0.2, 0, 0, 1) | 140mscubic-bezier(0.2, 0, 0, 1) | |
| Swatch ringglides to the selected swatch (Sliding Indicator) | spring.smooth0.36s, bounce 0 | 0.36s, bounce 0 | |
| Hue thumbtracks the pointer 1:1, as every Slider | no token no easing | no easing | |
| Popover closes | duration.instantease.exit80ms · cubic-bezier(0.4, 0, 1, 1) | 80mscubic-bezier(0.4, 0, 1, 1) |
Reduced motion
The area thumb and the swatch ring jump to their new places, and the popover opens at once.
Reduced motion
The area thumb and the swatch ring move straight to their new places, and the popover appears without scaling. Dragging, typing and the eyedropper work the same.
AccessibilityKeyboard, ARIA, focus
Accessibility
- The trigger reads its label and the current value, such as "Brand color, #3554fc".
- The area is one focusable
sliderthumb whose value text names both axes. ArrowLeft and ArrowRight step saturation, ArrowUp and ArrowDown step brightness, and Shift steps by 10. - The hue slider is named "Hue" and reads its value in degrees.
- Swatches are toggle buttons in a group named "Swatches", each named by its
labeland markedaria-pressedwhen selected. - Escape closes the popover and returns focus to the trigger, as every Popover does.