ColorArea
A color area allows users to adjust two channels of an RGB, HSL or HSB color value against a two-dimensional gradient background.
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>
);
};