Cell Color Picker
A settings-list row that names a colour, shows its value, and opens a picker to change it.
Usage
CellColorPicker is one row of a settings list. The whole row is the button: pressing anywhere on
it opens a popover of colour controls. The row itself carries the label, the current value as text,
and the swatch it is set to.
It wraps the ColorPicker from
@blakeui/react, so the state, the overlay and the keyboard behaviour are React Aria's. What this
component adds is the cell-row layout, the controls the popover fills itself with, and the two
surface treatments that let a stack of these read as one settings panel.
Anatomy
Import the CellColorPicker component and access all parts using dot notation.
import {CellColorPicker} from "@blakeui/pro-react";
<CellColorPicker>
<CellColorPicker.Trigger>
<CellColorPicker.Label />
<CellColorPicker.ValueDisplay />
<CellColorPicker.Swatch />
</CellColorPicker.Trigger>
<CellColorPicker.Popover />
</CellColorPicker>;CellColorPicker.Trigger and CellColorPicker.Popover are required; the three parts inside the
trigger are each optional, and their order is yours.
An empty CellColorPicker.Popover supplies its own controls — a saturation/brightness area, a hue
slider and a hex field — the way Timeline supplies its rail. A settings list is usually several
colours long, and hand-composing three controls per row is work the component can do for you. Pass
children to replace them entirely.
Controlled
Selected: #E9573F
Pass value with onChange to drive the colour yourself, or defaultValue to set the initial
colour and leave the component to manage it. Both accept a CSS colour string or a parsed Color;
onChange always hands back a Color.
Disabled
isDisabled on the root disables the row and stops the popover opening. A
CellColorPicker.Trigger with its own isDisabled wins over the root's, so one cell in a group can
opt out of a disabled set.
Settings Group
Stacked cells are what the component is for. The panel paints nothing behind them and each cell
keeps its own stroke, matching how ItemCardGroup groups its cards.
Variants
variant sits on the root and reaches the row through context. Both treatments are bordered-flat —
a 1px stroke and no elevation — and only the fill separates them: default sits on --surface,
secondary on --surface-secondary.
With Presets
presets on the popover adds a swatch row beneath the controls. It writes to the same state as the
area and the slider, so a preset press updates the row immediately and the swatch matching the
current colour shows as selected.
CSS Classes
Element Classes
.cell-color-picker— The root.display: blockandwidth: 100%, so the cell spans its list rather than hugging its content the way the free.color-pickerdoes..cell-color-picker__trigger— The visible row, and the<button>.display: flex,justify-content: space-between,gap: calc(var(--spacing) * 3),border: 1px solid var(--border),border-radius: calc(var(--radius) * 2),padding-inline: calc(var(--spacing) * 4),padding-block: calc(var(--spacing) * 2.5),min-height: calc(var(--spacing) * 11)andtext-align: start. The44pxfloor is the Cell family's row height — a settings row is a touch target rather than a text field — and it is the floor rather than the padding that sets it, so a row whose content grows taller still lays itself out. It setsbox-shadow: none: the row is bordered-flat, so the stroke does the separating..cell-color-picker__label— The leading text.flex: 1 1 autowithmin-width: 0and an ellipsis, which is what pins the value and the swatch to the trailing edge as a pair.font-weight: 500,color: var(--foreground)..cell-color-picker__value-display— The colour as text.color: var(--muted)andfont-variant-numeric: tabular-nums, so the row does not twitch as the digits change during a drag..cell-color-picker__swatch— The colour chip.calc(var(--spacing) * 5)square,border-radius: calc(var(--radius) * 0.75), andbox-shadow: inset 0 0 0 1px var(--border)— an inset ring rather than a border, so a pale colour stays visible without losing any of the painted area..cell-color-picker__popover— The overlay.min-width: calc(var(--spacing) * 62), wide enough for an area and its sliders to share one column. It inheritsbox-shadow: var(--shadow-overlay)from.color-picker__popoverand keeps it: this is a genuinely floating surface..cell-color-picker__presets— The preset swatch row, present only whenpresetsis passed.border-top: 1px solid var(--separator)withpadding-top: calc(var(--spacing) * 3): picking a preset is a different kind of choice from dragging the area, and the rule says so..cell-color-picker__dialog— The dialog inside the popover.display: flex,flex-direction: column,gap: calc(var(--spacing) * 3). Internal toCellColorPicker.Popoverrather than a part you compose.
The free .color-picker, .color-picker__trigger and .color-picker__popover classes are on the
same elements. The Pro layer is imported after @blakeui/styles, so where the two set the same
property at equal specificity the Pro rule wins on source order.
Motion
The row's own feedback is the free trigger's background-colour transition. It is colour-only, so there is no movement in it to remove and it needs no reduced-motion opt-out.
The popover's entrance and exit belong to @blakeui/styles, not to this component:
[data-entering="true"] runs enter at 150ms on --ease-out-quint with a one---spacing
directional slide, and [data-exiting="true"] runs exit at 100ms on --ease-out-quad. Its
reduced-motion opt-out ships there too, as a motion-reduce: utility that the package's own variant
expands to both halves — an explicit [data-reduce-motion="true"] on the popover or any ancestor,
and @media (prefers-reduced-motion: reduce) scoped so the two never double-apply. An overlay motion
change is a version bump of the free package, never a rule here.
API Reference
CellColorPicker
The root. Wraps the free ColorPicker, which owns the colour state and the overlay trigger, and
hands variant and isDisabled to the parts through context. Arbitrary DOM props are not accepted:
the free root forwards its rest props to React Aria's state container, which renders none of them.
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | A CellColorPicker.Trigger and a CellColorPicker.Popover. Required. |
value | Color | string | — | Controlled colour. Pair it with onChange. |
defaultValue | Color | string | — | Initial colour when uncontrolled. |
onChange | (color: Color) => void | — | Called whenever the colour changes — a drag on the area or a slider, a hex edit, or a preset press. |
variant | "default" | "secondary" | "default" | Surface treatment of the row. Both are bordered-flat; only the fill differs. Applied to CellColorPicker.Trigger through context. |
isDisabled | boolean | false | Disables the cell. A CellColorPicker.Trigger with its own isDisabled wins over this. |
className | string | — | Class name for the root. |
CellColorPicker.Trigger
The visible row, and the only interactive element in the cell. It is the free
ColorPicker.Trigger — a React Aria Button
under a DialogTrigger — so it carries a button role and aria-expanded natively, and accepts all
of that component's props. Its accessible name comes from its own text content.
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Row content — a Label, and typically a ValueDisplay and a Swatch. Required. |
isDisabled | boolean | — | Disables this row, overriding the root's own value. |
className | string | (renderProps) => string | — | Class name for the row. A function receives React Aria's button render props. |
CellColorPicker.Label
The leading text. A <span> rather than a <label>: it sits inside the trigger button, so it is
already part of that button's accessible name, and label-for-a-button is not a relationship the
platform has. Accepts every native <span> attribute.
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | — | Label text. Required. |
className | string | — | Class name for the label. |
CellColorPicker.ValueDisplay
The current colour as text, read live from React Aria's ColorPickerStateContext. It tracks a drag
on the area without any local state, and it stays real text rather than decoration: it is the only
part of the row that says what the colour is, and it forms part of the trigger's accessible name.
Accepts every native <span> attribute except children.
| Prop | Type | Default | Description |
|---|---|---|---|
format | ColorFormat | "hex" | Colour notation to print — "hex", "hexa", "rgb", "rgba", "hsl", "hsla", "hsb" or "hsba". |
className | string | — | Class name for the value text. |
CellColorPicker.Swatch
The colour chip. Wraps the free
ColorSwatch and accepts all of its props;
it reads the current colour from the context React Aria's ColorPicker publishes, so it needs no
color of its own. Decorative by construction — the value display beside it carries the same
information as text.
| Prop | Type | Default | Description |
|---|---|---|---|
color | Color | string | — | Overrides the colour drawn. Left unset — which is the usual case — the swatch follows the picker's state. |
className | string | (renderProps) => string | — | Class name for the swatch. |
CellColorPicker.Popover
The overlay. Wraps the free ColorPicker.Popover — itself a React Aria
Popover — and accepts all of its props.
It renders an element with role="dialog" inside, which is what gives the overlay its role and its
accessible name. The popover portals to <body>, so it escapes any overflow clipping between it
and the row.
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | an area, a hue slider and a hex field | Replaces the supplied controls. Compose ColorArea, ColorSlider or ColorField from @blakeui/react; each reads and writes the picker's state through React Aria's context. |
presets | string[] | — | Colours offered as a swatch row beneath the controls. Kept alongside children rather than replaced by them. |
placement | Placement | "bottom end" | Preferred side, so the panel lines up under the swatch rather than under the label. React Aria flips it when the chosen side would overflow the viewport, and data-placement reports where it actually landed. |
aria-label | string | "Color picker" | Accessible name for the popover dialog. |
className | string | (renderProps) => string | — | Class name for the popover. |
Trend Chip
A compact chip that pairs a number with a direction — a caret, a semantic colour, and optional prefix and suffix text, in three sizes and four fills.
Cell Select
A select dressed as a settings row — a leading label, the current value at the trailing edge, and a list that opens under the value rather than under the label.