@xsolla/xui-color-picker
v0.185.6
Published
A cross-platform React color picker component with a visual selection area, format switching, and color value controls. <!-- BEGIN:xui-mcp-instructions:color-picker --> A compound component that lets users select a colour through a combination of a gradie
Readme
Color Picker
A cross-platform React color picker component with a visual selection area, format switching, and color value controls.
A compound component that lets users select a colour through a combination of a gradient canvas, hue and alpha sliders, a numeric input bar, and an optional eyedropper. Supports four colour formats and an optional alpha channel. Designed to be embedded in a popover, modal, or panel triggered by a colour swatch button.
When to use
- When a user needs to pick an arbitrary colour — theme customisation, avatar backgrounds, design tools, data visualisation palettes
- When the product supports branded colour selection with precise numeric input
- When alpha (opacity) control is part of the feature requirement
When not to use
- When the user must choose from a fixed set of colours — use a Select with colour options instead
- When space is constrained and a full picker is not feasible — offer a hex input field paired with a colour swatch preview
- As an inline always-visible element on a page — ColorPicker is heavy; embed it in a popover triggered by a swatch button
Content guidelines
- Trigger button — the swatch that opens ColorPicker must show the current selected colour as its fill. Pair it with a label or tooltip ("Choose colour") for clarity.
- Format selector — no visible label is needed inside the Select; the format name itself (HEX, RGB, etc.) acts as the label.
- Input placeholders — for HEX use #000000; for channel fields use 0. Do not use descriptive placeholder text inside narrow numeric inputs — it will be cut off.
- Extra content heading — if the slot contains a palette or team swatches, add a short section label above the slot in the popover header, not inside the slot itself.
Behaviour guidelines
- Canvas interaction — clicking anywhere on the colour canvas moves the handle to that position and updates all fields immediately. The handle must be draggable with both mouse and touch. Dragging outside the canvas bounds clamps the value to the nearest edge rather than throwing an error.
- Hue slider — dragging the hue handle updates the canvas background gradient in real time. The current saturation and brightness positions on the canvas are preserved when hue changes so the colour shifts smoothly.
- Alpha slider — the track background is a gradient from transparent to the current fully-opaque colour, overlaid on a checkerboard pattern. Moving the handle updates the A channel across all input fields.
- Format switching — switching the format in the Select reformats the input field values to the new format. The colour itself does not change; canvas and slider positions are preserved.
- Manual input — edits in any input field should update the canvas and sliders in real time as the user types, but commit only on blur, Enter, or Tab. Invalid entries (out-of-range values, malformed HEX strings) are silently clamped or rejected, restoring the last valid value on blur.
- HEX normalisation — accept 3-character shorthand (#F50 → #FF5500) and 6-/8-character full form. Normalise to uppercase on commit.
- Copy action — the copy Icon button copies the current colour value to the clipboard in the active format string (e.g. #FF5500, rgb(255, 85, 0), hsl(20, 100%, 50%)). Show a brief confirmation (tooltip or button state change) after copy.
- Eyedropper flow — after sampling, focus returns to the ColorPicker and all fields update to the picked colour. The format selector retains its current value.
- Magnifier — the internal .item-colorMagnifier sub-component appears during eyedropper sampling and shows a zoomed pixel grid around the cursor. It has no configurable props at the ColorPicker level.
- Popover placement — ColorPicker is typically opened from a small colour swatch trigger button. Position the popover so it does not clip the viewport; prefer bottom-start or bottom-end, with top-start / top-end as fallback. The popover width is fixed at 380 px.
- Closing — the picker closes on: clicking outside the popover, pressing Escape, or an explicit confirm/cancel action if the integration requires it. Live-apply vs confirm-on-close is a product decision — document the chosen behaviour per feature.
Accessibility
- The colour canvas handle must have role="slider" with aria-label="Colour" and paired aria-valuenow / aria-valuemin / aria-valuemax attributes for both the horizontal (saturation) and vertical (brightness/lightness) axes. Expose both axes as separate sliders if a single 2D handle is not supported by the target assistive technology.
- Hue and alpha sliders must each carry role="slider" with aria-label="Hue" / aria-label="Opacity" and aria-valuetext showing the human-readable value (e.g. "120°", "75%").
- Each numeric input field must have a programmatic label (aria-label) identifying the channel — "Red", "Green", "Blue", "Alpha", "Hue", "Saturation", "Lightness", "Hex colour", etc.
- The format selector must have aria-label="Colour format".
- The eyedropper button must have aria-label="Pick colour from screen".
- The copy button must have aria-label="Copy colour value".
- If the EyeDropper API is unavailable, hide the button entirely rather than disabling it without explanation.
Eyedropper
- When Eyedropper=True, an eyedropper Icon button appears to the left of the sliders. Clicking it activates the native EyeDropper API (where supported), allowing the user to sample any pixel on screen. The picked colour immediately populates all fields and updates the canvas.
Browser support for the EyeDropper API must be checked at runtime. When unsupported, hide the button entirely (Eyedropper=False) rather than showing a disabled control with no explanation.
Keyboard navigation
- Tab / Shift+Tab
- Move focus between canvas handle, sliders, format selector, and input fields
- Arrow keys (canvas focused)
- Move the colour handle 1 px in the pressed direction
- Arrow keys (slider focused)
- Increment / decrement hue or alpha by 1 unit
- Enter / Space (eyedropper button)
- Activate eyedropper
- Enter (input focused)
- Commit the typed value
- Tab (input focused)
- Commit value and move focus to the next field
- Escape
- Close the popover (if open) without committing uncommitted input
Installation
npm install @xsolla/xui-color-pickerDemo
Basic Color Picker
import * as React from "react";
import { ColorPicker } from "@xsolla/xui-color-picker";
export default function BasicColorPicker() {
const [color, setColor] = React.useState("#66E6FFFF");
return (
<ColorPicker
value={color}
onChange={({ valueInInitialFormat }) => setColor(valueInInitialFormat)}
/>
);
}Without Alpha Channel
import * as React from "react";
import { ColorPicker } from "@xsolla/xui-color-picker";
export default function NoAlpha() {
return <ColorPicker value="#FF5733" alpha={false} onChange={console.log} />;
}With Eyedropper
import * as React from "react";
import { ColorPicker } from "@xsolla/xui-color-picker";
export default function WithEyedropper() {
return <ColorPicker value="#3498DB" eyedropper onChange={console.log} />;
}Anatomy
import { ColorPicker } from "@xsolla/xui-color-picker";
<ColorPicker
value="#66E6FFFF" // Initial color value
colorFormat="hex" // Default format (hex, rgb, hsl, hsb)
alpha={true} // Show alpha slider
eyedropper={false} // Show eyedropper button
selectableFormats={["hex", "hsl", "rgb", "hsb"]} // Available formats
onChange={handleChange} // Change callback
bottomContent={<Custom />} // Custom content below controls
/>;Examples
Custom Format Selection
import * as React from "react";
import { ColorPicker } from "@xsolla/xui-color-picker";
export default function CustomFormats() {
return (
<ColorPicker
value="#FF0000"
colorFormat="rgb"
selectableFormats={["rgb", "hex"]}
onChange={({ valueInCurrentFormat, currentColorFormat }) => {
console.log(`${currentColorFormat}: ${valueInCurrentFormat}`);
}}
/>
);
}With Bottom Content
import * as React from "react";
import { ColorPicker } from "@xsolla/xui-color-picker";
import { Button } from "@xsolla/xui-button";
export default function WithBottomContent() {
const [color, setColor] = React.useState("#66E6FFFF");
return (
<ColorPicker
value={color}
onChange={({ valueInInitialFormat }) => setColor(valueInInitialFormat)}
bottomContent={
<Button size="sm" onPress={() => console.log("Applied:", color)}>
Apply Color
</Button>
}
/>
);
}Controlled Color Picker
import * as React from "react";
import { ColorPicker } from "@xsolla/xui-color-picker";
export default function ControlledPicker() {
const [color, setColor] = React.useState("#9B59B6FF");
const handleChange = ({
valueInInitialFormat,
valueInCurrentFormat,
currentColorFormat,
}) => {
console.log("Format:", currentColorFormat);
console.log("Current format value:", valueInCurrentFormat);
console.log("Initial format value:", valueInInitialFormat);
setColor(valueInInitialFormat);
};
return (
<div>
<p>Selected: {color}</p>
<ColorPicker value={color} onChange={handleChange} />
</div>
);
}API Reference
ColorPicker
ColorPickerProps:
| Prop | Type | Default | Description |
| :---------------- | :---------------------------------------- | :------------ | :------------------------------------------------------------------------------------------------------------ |
| testID | string | — | Test ID for testing frameworks. On web this renders as data-testid; on React Native it renders as testID. |
| value | string | "#66E6FFFF" | Color value in any supported format. |
| colorFormat | "hex" \| "rgb" \| "hsl" \| "hsb" | "hex" | Default color format for output. |
| alpha | boolean | true | Show alpha channel slider. |
| eyedropper | boolean | false | Show eyedropper tool (web only). |
| selectableFormats | Array<"hex" \| "rgb" \| "hsl" \| "hsb"> | All formats | Available formats in dropdown. |
| copiedIcon | ReactNode | - | Custom icon for copied state. |
| bottomContent | ReactNode | - | Custom content below color controls. |
| onChange | (result: ColorChangeResult) => void | - | Callback when color changes. |
| testID | string | - | Test identifier. |
ColorChangeResult:
interface ColorChangeResult {
valueInInitialFormat: string; // Color in original format
valueInCurrentFormat: string; // Color in selected format
currentColorFormat: "hex" | "rgb" | "hsl" | "hsb";
}Supported Formats
| Format | Example | With Alpha |
| :----- | :------------------- | :----------------------- |
| hex | #FF5733 | #FF5733FF |
| rgb | rgb(255, 87, 51) | rgba(255, 87, 51, 1) |
| hsl | hsl(11, 100%, 60%) | hsla(11, 100%, 60%, 1) |
| hsb | hsb(11, 80%, 100%) | hsba(11, 80%, 100%, 1) |
Features
- Visual picker: Click and drag on the color area to select hue/saturation
- Sliders: Hue and alpha channel sliders
- Format switching: Toggle between hex, RGB, HSL, HSB via dropdown
- Copy button: Copy current color value to clipboard
- Reset button: Reset to initial color value
- Eyedropper: Pick colors from anywhere on screen (web only, requires browser support)
Accessibility
- Copy and reset buttons have accessible labels
- Keyboard navigation supported for all controls
- Color values are displayed in text inputs for manual entry
