About
HeroUI v3 ColorArea is a 2D color picker surface for adjusting two channels of a color at once (e.g. saturation/brightness) via a draggable thumb. Built on the real @heroui/react package with @heroui/styles, it supports controlled and uncontrolled value, disabled state, selectable color space & channels, a dotted-grid variant, and a custom render function. A thin re-export of the upstream component so consumers install and render the genuine HeroUI primitive.
Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds HeroUI Color Area to your project and fits it to your theme.
Code
How HeroUI Color Area is used: the demo's code. The component's own source comes with a free account.
Usage.tsx
"use client"
import type {ColorSpace, Key} from "@heroui/react";
import { Label, ListBox, Select, parseColor } from "@heroui/react"
import { ColorArea } from "@/components/ui/heroui-color-area"
import {useState} from "react";
type ColorChannel = "hue" | "saturation" | "brightness" | "lightness" | "red" | "green" | "blue";
interface ChannelOption {
id: ColorChannel;
name: string;
}
const colorSpaces: Array<{id: ColorSpace; name: string}> = [
{id: "rgb", name: "RGB"},
{id: "hsl", name: "HSL"},
{id: "hsb", name: "HSB"},
];
const channelsBySpace: Record<ColorSpace, ChannelOption[]> = {
hsb: [
{id: "hue", name: "Hue"},
{id: "saturation", name: "Saturation"},
{id: "brightness", name: "Brightness"},
],
hsl: [
{id: "hue", name: "Hue"},
{id: "saturation", name: "Saturation"},
{id: "lightness", name: "Lightness"},
],
rgb: [
{id: "red", name: "Red"},
{id: "green", name: "Green"},
{id: "blue", name: "Blue"},
],
};
export function ColorAreaSpaceAndChannels() {
const [colorSpace, setColorSpace] = useState<ColorSpace>("hsb");
const [color, setColor] = useState(() => parseColor("hsb(219, 58%, 93%)"));
const channels = channelsBySpace[colorSpace];
const defaultX = colorSpace === "rgb" ? "blue" : "saturation";
const defaultY =
colorSpace === "rgb" ? "green" : colorSpace === "hsl" ? "lightness" : "brightness";
const [xChannel, setXChannel] = useState<ColorChannel>(defaultX);
const [yChannel, setYChannel] = useState<ColorChannel>(defaultY);
const handleColorSpaceChange = (newSpace: Key | null) => {
if (!newSpace) return;
const space = newSpace as ColorSpace;
setColorSpace(space);
// Reset channels to appropriate defaults for the new color space
if (space === "rgb") {
setXChannel("blue");
setYChannel("green");
} else if (space === "hsl") {
setXChannel("saturation");
setYChannel("lightness");
} else {
setXChannel("saturation");
setYChannel("brightness");
}
};
// Filter out the other channel from options (can't have same channel on both axes)
const xChannelOptions = channels.filter((c) => c.id !== yChannel);
const yChannelOptions = channels.filter((c) => c.id !== xChannel);
return (
<div className="flex flex-col items-center gap-6">
{/* Controls */}
<div className="flex gap-4">
{/* Color Space Select */}
<Select className="w-32" value={colorSpace} onChange={handleColorSpaceChange}>
<Label>Color Space</Label>
<Select.Trigger>
<Select.Value />
<Select.Indicator />
</Select.Trigger>
<Select.Popover>
<ListBox>
{colorSpaces.map((space) => (
<ListBox.Item key={space.id} id={space.id} textValue={space.name}>
{space.name}
<ListBox.ItemIndicator />
</ListBox.Item>
))}
</ListBox>
</Select.Popover>
</Select>
{/* X Channel Select */}
<Select
className="w-36"
value={xChannel}
onChange={(value) => value && setXChannel(value as ColorChannel)}
>
<Label>X Axis</Label>
<Select.Trigger>
<Select.Value />
<Select.Indicator />
</Select.Trigger>
<Select.Popover>
<ListBox>
{xChannelOptions.map((channel) => (
<ListBox.Item key={channel.id} id={channel.id} textValue={channel.name}>
{channel.name}
<ListBox.ItemIndicator />
</ListBox.Item>
))}
</ListBox>
</Select.Popover>
</Select>
{/* Y Channel Select */}
<Select
className="w-36"
value={yChannel}
onChange={(value) => value && setYChannel(value as ColorChannel)}
>
<Label>Y Axis</Label>
<Select.Trigger>
<Select.Value />
<Select.Indicator />
</Select.Trigger>
<Select.Popover>
<ListBox>
{yChannelOptions.map((channel) => (
<ListBox.Item key={channel.id} id={channel.id} textValue={channel.name}>
{channel.name}
<ListBox.ItemIndicator />
</ListBox.Item>
))}
</ListBox>
</Select.Popover>
</Select>
</div>
{/* Color Area */}
<ColorArea
colorSpace={colorSpace}
value={color}
xChannel={xChannel}
yChannel={yChannel}
onChange={setColor}
>
<ColorArea.Thumb />
</ColorArea>
{/* Color Value Display */}
<div className="flex items-center gap-3">
<div
className="size-8 rounded-md border border-default"
style={{backgroundColor: color.toString("css")}}
/>
<code className="rounded bg-default/50 px-2 py-1 text-sm">
{color.toString(colorSpace)}
</code>
</div>
</div>
);
}
export default ColorAreaSpaceAndChannels








