Color
Provides shared color state to every component inside it.
import { Color } from '@pbroom/color-kit/react';color.tsx:54function Color(props: ColorProps): Element;Accepts the same options as useColor (uncontrolled defaultColor, or controlled state + onChange). Descendants subscribe to the slices of state they read, so an uncontrolled provider does not rerender on color changes and a drag only rerenders the components that depend on it. Read it with useColorContext or useColorStoreSelector.
Props
From ColorProps; other attributes pass through to the element.
- children
ReactNode - Color components that share this provider's state.
- defaultColor?
string | Colordefault{ l: 0.6, c: 0.2, h: 250, alpha: 1 } - Initial requested color in uncontrolled mode: any CSS color string
parseaccepts, or an OKLCHColorobject. Ignored whenstateis set. - state?
ColorState - Controlled full state. When set, the hook renders this state and reports updates through
onChangeinstead of storing them. - onChange?
(event: ColorUpdateEvent) => void - Called when a setter produces a new state. Fires synchronously inside the setter call (i.e. in your event handler, before React re-renders) in both controlled and uncontrolled modes, exactly once per effective update and never for no-op updates. Several setter calls in one tick compose: each
event.nextbuilds on the previous one, even in controlled mode before the newstateprop arrives. Once React re-renders, later calls start from thestateyou committed, so an update you ignore is not carried into the next one. - defaultGamut?
GamutTargetdefault'display-p3' - Initial active display gamut in uncontrolled mode.
- defaultView?
ViewModeldefault'oklch' - Initial active view model in uncontrolled mode.
Example
import { Color, useColorContext, useColorPlaneRenderer } from '@pbroom/color-kit/react';
function Plane() {
const { requested } = useColorContext();
const { ref, canvasKey } = useColorPlaneRenderer({ color: requested });
return <canvas ref={ref} key={canvasKey} style={{ width: 240, height: 160 }} />;
}
function Hue() {
const { requested, setChannel } = useColorContext();
return (
<input
type="range"
aria-label="Hue"
min={0}
max={360}
value={requested.h}
onChange={(event) => setChannel('h', Number(event.currentTarget.value))}
/>
);
}
export const Picker = () => (
<Color defaultColor="#ff6600">
<Plane />
<Hue />
</Color>
);Edge cases
- The provider does not re-render.
<Color>passes a stable store through context and never re-renders its children on a color change; components re-render when the store slice they subscribe to changes. Reading the context withuseContext(ColorContext)gives that stable store and the setters, not live fields; useuseColorContextfor a live snapshot oruseColorStoreSelectorfor one field. - Controlled updates compose. With
stateandonChange,onChangefires synchronously inside each setter call, once per effective update and never for a no-op. Several setter calls in one event build on each other even before your newstatearrives. After React re-renders, the next update starts from thestateyou passed, so an update you chose to drop is not carried forward. defaultColoris read once. Changing it after mount has no effect, as with any uncontrolled input; passstateto drive the color from outside, or remount with akey.- Defaults. Without
defaultColorthe color isoklch(0.6 0.2 250); the active gamut defaults to'display-p3'and the view to'oklch'.