ColorArea
A sophisticated color area component for selecting 2D color values. Features accessible keyboard navigation, touch support, and seamless integration with other color primitives.
Basic Usage
#800080
hsb(300, 100%, 50%)
"use client";
import { useState } from "react";
import { parseColor, Color } from "react-aria-components";
import { ColorArea } from "@repo/ui/components/color-area";
import { Label } from "@repo/ui/components/label";
import {
Select,
SelectTrigger,
SelectContent,
SelectItem,
SelectValue,
} from "@repo/ui/components/select";
import { Switch } from "@repo/ui/components/switch";
type ColorSpace = "rgb" | "hsl" | "hsb";
type Channel =
| "red"
| "green"
| "blue"
| "hue"
| "saturation"
| "lightness"
| "brightness"
| "alpha";
const CHANNEL_OPTIONS: Record<ColorSpace, Channel[]> = {
rgb: ["red", "green", "blue", "alpha"],
hsl: ["hue", "saturation", "lightness", "alpha"],
hsb: ["hue", "saturation", "brightness", "alpha"],
};
const DEFAULT_COLORS: Record<ColorSpace, string> = {
rgb: "rgb(128, 0, 128)",
hsl: "hsl(300, 100%, 25%)",
hsb: "hsb(300, 100%, 50%)",
};
/* INTERACTIVE COLOR AREA PLAYGROUND */
export const Example1: React.FC = () => {
const [colorSpace, setColorSpace] = useState<ColorSpace>("hsb");
const [xChannel, setXChannel] = useState<string>("default");
const [yChannel, setYChannel] = useState<string>("default");
const [isDisabled, setIsDisabled] = useState(false);
const [color, setColor] = useState<Color>(parseColor(DEFAULT_COLORS.hsb));
const handleColorSpaceChange = (newSpace: string) => {
if (!newSpace || !DEFAULT_COLORS[newSpace as ColorSpace]) return;
setColorSpace(newSpace as ColorSpace);
setXChannel("default");
setYChannel("default");
setColor(parseColor(DEFAULT_COLORS[newSpace as ColorSpace]));
};
const channels = CHANNEL_OPTIONS[colorSpace];
return (
<div className="flex gap-8">
{/* Left: Controls */}
<div className="flex flex-col gap-4">
<div className="space-y-1.5">
<Label>colorSpace</Label>
<Select
defaultValue={colorSpace}
value={colorSpace}
onChange={(key) =>
handleColorSpaceChange(key as string)
}
>
<SelectTrigger className="w-32">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="rgb">RGB</SelectItem>
<SelectItem value="hsl">HSL</SelectItem>
<SelectItem value="hsb">HSB</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-1.5">
<Label>xChannel</Label>
<Select
defaultValue="default"
value={xChannel}
onChange={(key) => setXChannel(key as string)}
>
<SelectTrigger className="w-32">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="default">Default</SelectItem>
{channels.map((ch) => (
<SelectItem key={ch} value={ch}>
{ch}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="space-y-1.5">
<Label>yChannel</Label>
<Select
defaultValue="default"
value={yChannel}
onChange={(key) => setYChannel(key as string)}
>
<SelectTrigger className="w-32">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="default">Default</SelectItem>
{channels.map((ch) => (
<SelectItem key={ch} value={ch}>
{ch}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex items-center gap-2">
<Switch isSelected={isDisabled} onChange={setIsDisabled} />
<Label>isDisabled</Label>
</div>
</div>
{/* Right: ColorArea + Output */}
<div className="flex flex-col gap-4">
<ColorArea
value={color}
onChange={setColor}
xChannel={
xChannel === "default"
? undefined
: (xChannel as Channel)
}
yChannel={
yChannel === "default"
? undefined
: (yChannel as Channel)
}
isDisabled={isDisabled}
/>
<div className="flex items-center gap-3">
<div
className="size-10 rounded-md border shadow-sm"
style={{ backgroundColor: color.toString("css") }}
/>
<div className="text-sm">
<p className="font-mono font-medium">
{color.toString("hex")}
</p>
<p className="text-muted-foreground font-mono">
{color.toString(colorSpace)}
</p>
</div>
</div>
</div>
</div>
);
};
Controlled
#00FF00
hsl(120, 100%, 50%)
"use client";
import { useState } from "react";
import { parseColor } from "react-aria-components";
import { ColorArea } from "@repo/ui/components/color-area";
/* CONTROLLED COLOR AREA WITH VALUE DISPLAY */
export const Example2: React.FC = () => {
const [color, setColor] = useState(parseColor("hsl(120, 100%, 50%)"));
return (
<div className="flex flex-col gap-4">
<ColorArea value={color} onChange={setColor} />
<div className="flex items-center gap-3">
<div
className="size-10 rounded-md border shadow-sm"
style={{ backgroundColor: color.toString("css") }}
/>
<div className="text-sm">
<p className="font-medium">{color.toString("hex")}</p>
<p className="text-muted-foreground">
{color.toString("hsl")}
</p>
</div>
</div>
</div>
);
};
X/Y Channel Configuration
Use xChannel and yChannel props to configure which color channels are controlled by the horizontal and vertical axes.
Saturation vs Lightness
Hue vs Saturation
hsl(0, 100%, 50%)
"use client";
import { useState } from "react";
import { parseColor } from "react-aria-components";
import { ColorArea } from "@repo/ui/components/color-area";
/* COLOR AREA WITH DIFFERENT COLOR SPACES */
export const Example3: React.FC = () => {
const [hslColor, setHslColor] = useState(parseColor("hsl(0, 100%, 50%)"));
return (
<div className="flex flex-col gap-4">
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<p className="text-sm font-medium">
Saturation vs Lightness
</p>
<ColorArea
value={hslColor}
onChange={setHslColor}
xChannel="saturation"
yChannel="lightness"
/>
</div>
<div className="space-y-2">
<p className="text-sm font-medium">Hue vs Saturation</p>
<ColorArea
value={hslColor}
onChange={setHslColor}
xChannel="hue"
yChannel="saturation"
/>
</div>
</div>
<div className="flex items-center gap-3">
<div
className="size-10 rounded-md border shadow-sm"
style={{ backgroundColor: hslColor.toString("css") }}
/>
<p className="text-sm font-medium">
{hslColor.toString("hsl")}
</p>
</div>
</div>
);
};
Disabled
"use client";
import { parseColor } from "react-aria-components";
import { ColorArea } from "@repo/ui/components/color-area";
/* DISABLED COLOR AREA */
export const Example4: React.FC = () => {
return <ColorArea defaultValue={parseColor("#7f007f")} isDisabled />;
};
Playground
Try out different color spaces (RGB, HSL, HSB) and channel configurations:
"use client";
import { parseColor } from "react-aria-components";
import { ColorArea } from "@repo/ui/components/color-area";
/* BASIC COLOR AREA EXAMPLE */
export const Example5: React.FC = () => {
return <ColorArea defaultValue={parseColor("#7f007f")} />;
};
Component Code
"use client";
import {
ColorArea as AriaColorArea,
ColorAreaProps as AriaColorAreaProps,
} from "react-aria-components";
import { ColorThumb } from "@repo/ui/components/color-thumb";
import { cn } from "tailwind-variants";
export interface ColorAreaProps extends AriaColorAreaProps {}
export const ColorArea: React.FC<ColorAreaProps> = ({
className,
...props
}) => {
return (
<AriaColorArea
data-slot="color-area"
{...props}
className={cn(
"data-disabled:bg-muted aspect-square w-56 shrink-0 rounded-lg",
className,
)}
style={({ defaultStyle, isDisabled }) => ({
...defaultStyle,
background: isDisabled ? undefined : defaultStyle.background,
})}
>
<ColorThumb />
</AriaColorArea>
);
};
Checkbox
An accessible checkbox component for forms and selection lists. Supports indeterminate states, custom styling, and keyboard navigation, built on top of React Aria primitives.
ColorWheel
An interactive circular color wheel for selecting hue values. Supports keyboard operation, touch input, and precise control, ideal for building comprehensive color pickers.