# ColorPicker A colour, chosen or typed. - Group: Forms - Import: `import { ColorPicker } from '@misoto22/design'` - Page: https://ui.misoto22.com/components/color-picker/ - Related: field, input, popover ## When to reach for it A person is choosing the colour. A colour that is merely being SHOWN is a swatch, and a set of fixed brand colours is a RadioGroup — a picker offers sixteen million answers to a question with six. ## Anatomy - **Trigger** (required) — The closed control: a swatch, then the value as text, in the same box as Input and Select. Named by its label AND by its own value, so a reader hears “Brand colour, #a78bfa” rather than either half. - **Swatch** — The colour over a checkerboard, so a half-transparent value reads as transparent rather than as a paler colour. - **Notation strip** — Hex, OKLCH and Display P3, as a single-value ToggleGroup. It changes what onValueChange emits, not what the colour is — and hex and P3 are bounded, so switching to one of them fits the colour to that gamut on the way out. - **Field** (required) — The plane: chroma across, lightness up. Each ROW is normalised to the most chroma that exists at that lightness and hue, so the whole surface is reachable instead of a lens of colour inside bands of clipped duplicates. Underneath it are two real sliders rather than key handlers on a canvas, which is what gives it arrows, Home, End and an announced position. - **Hue track** — A ramp taken at the lightness and chroma already chosen, not a generic rainbow — so the strip shows the hues of THIS colour rather than of some other one. - **Opacity track** — Transparent to the current colour, over the same checkerboard as the swatch. - **CSS box** — The value as text. Accepts hex, rgb(), hsl(), oklch() and color(display-p3 …) in both syntaxes, and paints itself invalid on anything else. ## Best practices ### Do - Wrap it in a Field. The trigger is a button, which a label binds to and clicks through — one of the few composites where that works without help. - Pass the notation you want back. The panel emits in whatever notation the value arrived in until somebody changes it in the strip, so a defaultValue of "#a78bfa" keeps a consumer in hex. - Reach for this over when the colours are being TUNED. The native picker works in HSV, where a row of constant lightness visibly darkens as it saturates — so a reader building a palette is fighting the instrument. OKLCH is the space where two colours at the same height genuinely match. ### Don’t - Do not pass a named colour. Hex, rgb(), hsl(), oklch() and color(display-p3 …) parse; "rebeccapurple" does not, and the box will show it as invalid. Resolving names needs a table of every CSS keyword or a live DOM, and a picker that takes some names and not others is worse than one that takes none. - Do not read the emitted string as a fixed notation. It is whatever the strip is set to, so a consumer that slices a "#" off the front breaks the first time a reader picks OKLCH. - Do not use it to pick text or background colour and call the result accessible. Nothing here measures contrast; a picker that lets a reader choose #eeeeee for body copy is doing exactly what it was asked. ## Accessibility - label is required. The trigger shows a value, and a value is not a name. - The plane is a group of two real sliders — Chroma and Lightness — each announced with a percentage, so the 2D surface is operable and reported rather than merely clickable. - The focus ring is drawn on the plane, because the sliders that take the focus are visually hidden and the browser’s own ring is clipped away with them. - Alpha is doubled by a checkerboard everywhere it is shown, so transparency is not carried by lightness alone. ## Keyboard - Enter / Space — Opens the panel. - ← / → — Moves the focused axis or track by one step. - Home / End — Jumps that axis or track to its ends. - Escape — Closes the panel; focus returns to the trigger. ## ColorPicker A colour, chosen or typed. The panel works in OKLCH, and that is the reason to reach for this rather than ``. In HSV — which is what the native picker and most libraries use — dragging along a row of constant "lightness" walks through colours the eye reads as getting darker, so a reader tuning a palette is fighting the instrument. OKLCH's lightness is the one a person sees, so two colours at the same height on the plane genuinely match, and the hue strip stays at the lightness already chosen instead of showing a rainbow that belongs to some other colour. The plane is normalised to the gamut ROW BY ROW: its right edge is the most chroma that exists at that lightness and hue, so the whole surface is reachable rather than a lens of colour inside a field of clipped duplicates. **What it accepts.** Hex, `rgb()`, `hsl()`, `oklch()` and `color(display-p3 …)`, in both syntaxes. Named colours are not accepted: resolving them needs a table of every CSS name or a live DOM, and a box that takes "rebeccapurple" but not "papayawhip" is worse than one that takes neither. **Keyboard.** The plane is a group of two real sliders — chroma across, lightness up — so the arrows move it and a screen reader announces where it is. That is the part a 2D canvas usually leaves out, and leaving it out makes the control unusable rather than merely awkward. ### Props - `label` (required) — `string`. Names the control. Required — the trigger shows a colour, and a colour is not a name. Announced together with the value, the way `Select`'s is: a reader hears "Brand colour, #a78bfa". Inside a `Field` with a label, that label is used and this one is not repeated. - `value` — `string`. The colour, as any absolute CSS colour string. - `defaultValue` — `string` default `'#000000'`. - `onValueChange` — `(value: string) => void`. Fires with a CSS colour string in whichever notation the panel is set to — so the notation a caller passes in is the notation they get back until somebody changes it in the panel. - `disabled` — `boolean`. - `invalid` — `boolean`. Paints the resting border with `--danger` and reflects `aria-invalid`. - `className` — `string`. - `id` — `string`. The TRIGGER's id — the element a label points at. A `Field` sets it. - `aria-describedby` — `string`. Ids of the copy describing the control. A `Field` sets it from hint, error and description. - `aria-invalid` — `boolean | 'true' | 'false'`. The spelling a form library sets; read together with `invalid`. - `aria-required` — `boolean`. Announced on the trigger. A `Field` sets it from `required`. ## Example — default ```tsx import { ColorPicker, Field } from '@misoto22/design'
```