ComponentsForm
Color picker
Lets people choose a colour on a saturation and brightness area, a hue slider and a hex field.
Import
import { ColorPicker, ColorPickerPopover, ColorSwatch } from "@booleanpress/ui/color-picker"Usage
ColorPicker draws the picker in the page; ColorPickerPopover draws a swatch button that opens it in a popover. Both take the same props.
import { ColorPicker, ColorPickerPopover } from "@booleanpress/ui/color-picker"
export function ButtonColour() {
return (
<>
<ColorPicker defaultValue="#276def" aria-label="Button colour" />
<ColorPickerPopover defaultValue="#276def" aria-label="Link colour" />
</>
)
}The value is a hex string (#rgb, #rrggbb, with or without alpha); a value that is not hex is ignored and the picker shows black or the colour it had. It is uncontrolled with defaultValue (#000000 by default), or controlled with value and onValueChange, which fires while the colour moves; onValueCommit fires once, when a drag, a key press or an edit of the hex field ends, which suits saving. alpha adds an alpha slider; the value then reads #rrggbbaa while the colour is not opaque. swatches adds preset colours under the picker: hex strings, or { value, label } to give each a spoken name. orientation="vertical" stands the hue (and alpha) slider beside the area. size and variant set the hex field's size and look and follow the provider; name submits the value in a form, as lower-case hex; a reset of the form brings back defaultValue, and a disabled picker is not submitted. Name the picker with aria-label or aria-labelledby; it is "Colour picker" by default, the provider string colorPicker. ColorSwatch draws a colour square on its own, for a list of saved colours.
Examples
Basic
The area, the hue slider, a preview and the hex field, in the page.
In a popover
ColorPickerPopover opens the picker from a 36 px swatch button named with the label and the colour.
import { ColorPickerPopover } from "@booleanpress/ui/color-picker"
export default function ColorPickerInPopover() {
return (
<div className="flex items-center gap-3">
<ColorPickerPopover defaultValue="#ff0000" aria-label="Accent colour" />
<span className="text-sm/normal text-foreground">Accent colour</span>
</div>
)
}Vertical hue slider
orientation="vertical" stands the hue and alpha sliders beside the area, the highest value at the top.
Controlled
value and onValueChange show the hex while it moves; onValueCommit reports it once each change ends.
import { useState } from "react"
import { ColorPicker } from "@booleanpress/ui/color-picker"
export default function ColorPickerControlled() {
const [colour, setColour] = useState("#0f766e")
const [saved, setSaved] = useState("#0f766e")
return (
<div className="flex w-full max-w-xs flex-col gap-3">
<p className="text-center font-mono text-sm/normal text-muted-foreground">
onValueChange: {colour}
<br />
onValueCommit: {saved}
</p>
<ColorPicker value={colour} onValueChange={setColour} onValueCommit={setSaved} aria-label="Email header colour" />
</div>
)
}Swatches
swatches adds preset brand colours, each named by its label; the one matching the colour is chosen.
import { ColorPicker } from "@booleanpress/ui/color-picker"
const BRAND = [
{ value: "#020617", label: "Ink" },
{ value: "#2563eb", label: "Mailer blue" },
{ value: "#0f766e", label: "Teal" },
{ value: "#16a34a", label: "Delivered green" },
{ value: "#ca8a04", label: "Queued amber" },
{ value: "#dc2626", label: "Bounce red" },
{ value: "#9333ea", label: "Violet" },
]
export default function ColorPickerSwatches() {
return <ColorPicker defaultValue="#2563eb" swatches={BRAND} aria-label="Brand colour" />
}With alpha
alpha adds an alpha slider over checks; the value carries an alpha pair.
Disabled
disabled dims the picker and takes its sliders and field out of the tab order.
Accessibility
- Semantics
- The picker is a
role="group". The area follows React Aria's ColorArea: its thumb holds two visually hidden range inputs, saturation (across) and brightness (up), so each is asliderwith its own name and value text. The hue and alpha sliders are range inputs too, so touch screen readers can swipe them. The hex field is a text input; the swatches are a radio group. - Labels
- Name the picker with
aria-labeloraria-labelledby("Colour picker" by default). The sliders are named "Saturation", "Brightness", "Hue" and "Alpha", the field "Hex colour" and the swatches "Preset colours" (the provider stringssaturation,brightness,hue,alpha,hexColorandcolorSwatches); values are read as percentages and degrees in the provider's locale. A swatch reads itslabel, else its hex. - Focus
- Tab moves through the area (one stop: the input used last, saturation at first), the hue slider, the alpha slider, the hex field and the swatches (one stop). A thumb shows the 1px
--ringoutline 2px outside while it has keyboard focus. In a popover, focus moves into the picker on open and back to the swatch button on close. - Known limits
- Colour is chosen by sight. The hex field and the swatches' names are the ways to choose an exact colour without it.
- There is no eyedropper and no other format than hex (RGB, HSL); convert the hex value yourself.
- In right-to-left pages the area and the horizontal sliders run from right to left, and the arrow keys follow them.
Keyboard
| Key | Behaviour |
|---|---|
| ←or→ | On the area, lowers or raises the saturation by 1 and moves focus to the saturation input; on a slider, lowers or raises its value. Swapped in right-to-left pages, where the area and the sliders run from right to left. |
| ↑or↓ | On the area, raises or lowers the brightness by 1 and moves focus to the brightness input; on a slider, raises or lowers its value. |
| Shift→ | With any arrow key, moves 10 that way. |
| Page UporPage Down | On the area, raises or lowers the brightness by 10; on a slider, its value by 10. |
| HomeorEnd | On the area, lowers or raises the saturation by 10 (as React Aria's ColorArea); on a slider, sets its minimum or maximum. |
| Enter | In the hex field, applies the colour typed and shows its hex again. |
| ←or→or↑or↓ | In the swatches, chooses the previous or next preset colour. |
API
ColorPicker
| Prop | Type | Default | Description |
|---|---|---|---|
alpha | boolean | false | Adds an alpha slider; the value then carries an alpha pair (#rrggbbaa) when the colour is not opaque. |
defaultValue | string | #000000 | The colour at the start, when it controls itself. #000000 by default. |
disabled | boolean | false | Dims the picker and ignores the pointer and the keyboard. |
form | string | The id of the form the value belongs to, for a control placed outside it. | |
name | string | The form field name; the hex value is submitted with the form. | |
onValueChange | ((value: string) => void) | Called with the new hex colour while it changes. | |
onValueCommit | ((value: string) => void) | Called with the hex colour once a drag, a key press or an edit of the hex field ends. | |
orientation | "horizontal" | "vertical" | horizontal | vertical puts the hue (and alpha) slider upright beside the area. horizontal by default. |
size | "default" | "sm" | "lg" | The hex field's and swatches' size. Defaults to the provider's controlSize. | |
swatches | readonly (string | { value: string; label?: string; })[] | Preset colours shown as swatches under the picker: hex strings, or { value, label } to name them. | |
value | string | The colour as hex (#3b82f6, or #3b82f680 with alpha), when you control it. | |
variant | "default" | "filled" | The hex field's look. Defaults to the provider's fieldVariant. |
ColorPickerPopover
| Prop | Type | Default | Description |
|---|---|---|---|
align | "center" | "start" | "end" | start | The popover's alignment against the swatch. start by default. |
alpha | boolean | false | Adds an alpha slider; the value then carries an alpha pair (#rrggbbaa) when the colour is not opaque. |
defaultOpen | boolean | Whether the popover starts open. | |
defaultValue | string | #000000 | The colour at the start, when it controls itself. #000000 by default. |
disabled | boolean | false | Dims the picker and ignores the pointer and the keyboard. |
form | string | The id of the form the value belongs to, for a control placed outside it. | |
name | string | The form field name; the hex value is submitted with the form. | |
onOpenChange | ((open: boolean) => void) | Called when the popover opens or closes. | |
onValueChange | ((value: string) => void) | Called with the new hex colour while it changes. | |
onValueCommit | ((value: string) => void) | Called with the hex colour once a drag, a key press or an edit of the hex field ends. | |
open | boolean | Whether the popover is open, when you control it. | |
orientation | "horizontal" | "vertical" | vertical puts the hue (and alpha) slider upright beside the area. horizontal by default. | |
side | "top" | "bottom" | "left" | "right" | The side of the swatch the popover opens on. Below by default. | |
size | "default" | "sm" | "lg" | The hex field's and swatches' size. Defaults to the provider's controlSize. | |
swatches | readonly (string | { value: string; label?: string; })[] | Preset colours shown as swatches under the picker: hex strings, or { value, label } to name them. | |
value | string | The colour as hex (#3b82f6, or #3b82f680 with alpha), when you control it. | |
variant | "default" | "filled" | The hex field's look. Defaults to the provider's fieldVariant. |
ColorSwatch
Renders a span and passes it every other prop.
| Prop | Type | Default | Description |
|---|---|---|---|
colorrequired | string | Any CSS colour: #3b82f6, #3b82f680, rgb(…). |
Every part takes className, merged with its defaults by cn(), and ref, which reaches the element it renders.
Data attributes: data-slot="color-picker-preview" (ColorPicker), data-slot="color-picker-trigger" (ColorPickerPopover), data-slot="color-swatch" (ColorSwatch), and data-orientation, data-size, data-disabled, data-channel.
Provider strings: hue, alpha, saturation, brightness, hexColor, colorPicker, colorSwatches (BooleanUIProvider's strings).
Theming
The area, the sliders and the swatches carry the colour itself; their edge is a 1px --border ring inside and the checks behind transparency are --muted on --background. The thumbs are 16 px circles with a white 3px ring in both themes, so they show on any colour. A chosen swatch has a 2px --ring ring 2px away. The hex field is an input.
The tokens its classes read. Change their values in your theme, and it follows.
| Token | Used for |
|---|---|
--ring | outline, focus ring |