Build a Color Swatch Group
Let's build a keyboard-navigable color swatch group for selecting colors from a palette.
Here's what we'll end up with:
Click to view the full code
tsx
import { useState } from "react";
import "internationalized-color/css";
import { ColorSwatchGroup } from "@urcolor/react";
const colors = [
"hsl(210, 80%, 50%)",
"hsl(350, 90%, 60%)",
"hsl(120, 60%, 45%)",
"hsl(45, 100%, 55%)",
"hsl(280, 70%, 55%)",
"hsl(15, 85%, 55%)",
];
export default function ColorSwatchGroupGuide() {
const [selected, setSelected] = useState<string[]>([colors[0]!]);
return (
<div className="flex flex-col gap-4">
<ColorSwatchGroup.Root
value={selected}
onValueChange={setSelected}
type="single"
className="flex items-center gap-2"
>
{colors.map((color) => (
<ColorSwatchGroup.Item
key={color}
value={color}
className="
flex size-10 cursor-pointer items-center justify-center rounded-lg
outline-none
"
>
<svg
className={`size-5 text-white drop-shadow-[0_1px_2px_rgba(0,0,0,0.5)] transition-opacity duration-150 ${selected.includes(color) ? "opacity-100" : "opacity-0"}`}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
>
<polyline points="20 6 9 17 4 12" />
</svg>
</ColorSwatchGroup.Item>
))}
</ColorSwatchGroup.Root>
<p className="text-sm text-[var(--vp-c-text-2)]">
Selected: <code>{selected[0] ?? "none"}</code>
</p>
</div>
);
}Step 1: Set up state
Define your color palette and selection state.
tsx
import { useState } from "react";
function MyGroup() {
const colors = [
"hsl(210, 80%, 50%)",
"hsl(350, 90%, 60%)",
"hsl(120, 60%, 45%)",
"hsl(45, 100%, 55%)",
];
const [selected, setSelected] = useState<string[]>([colors[0]!]);
}The selection is always an array of strings — even for single selection mode.
Step 2: Add the group root
ColorSwatchGroup.Root manages selection state and keyboard navigation.
tsx
import { useState } from "react";
import { ColorSwatchGroup } from "@urcolor/react";
function MyGroup() {
const colors = [
"hsl(210, 80%, 50%)",
"hsl(350, 90%, 60%)",
"hsl(120, 60%, 45%)",
"hsl(45, 100%, 55%)",
];
const [selected, setSelected] = useState<string[]>([colors[0]!]);
return (
<ColorSwatchGroup.Root
value={selected}
onValueChange={setSelected}
type="single"
className="flex items-center gap-2"
>
{/* items go here */}
</ColorSwatchGroup.Root>
);
}type="single"— only one color can be selected at a timetype="multiple"— multiple colors can be selected
Step 3: Add swatch items
ColorSwatchGroup.Item renders each selectable color. The value prop is both the selection value and the displayed color.
tsx
import { useState } from "react";
import { ColorSwatchGroup } from "@urcolor/react";
function MyGroup() {
const colors = [
"hsl(210, 80%, 50%)",
"hsl(350, 90%, 60%)",
"hsl(120, 60%, 45%)",
"hsl(45, 100%, 55%)",
];
const [selected, setSelected] = useState<string[]>([colors[0]!]);
return (
<ColorSwatchGroup.Root
value={selected}
onValueChange={setSelected}
type="single"
className="flex items-center gap-2"
>
{colors.map((color) => (
<ColorSwatchGroup.Item
key={color}
value={color}
className="
size-10 cursor-pointer rounded-lg
flex items-center justify-center outline-none
"
/>
))}
</ColorSwatchGroup.Root>
);
}TIP
All components are completely unstyled — the classes above are just an example using Tailwind CSS. Use any styling approach you prefer.
Multiple selection
Change type to "multiple" to allow selecting more than one color:
tsx
<ColorSwatchGroup.Root
value={selected}
onValueChange={setSelected}
type="multiple"
className="flex items-center gap-2"
>
{colors.map((color) => (
<ColorSwatchGroup.Item key={color} value={color} className="..." />
))}
</ColorSwatchGroup.Root>Keyboard navigation
The group supports full keyboard navigation out of the box:
- Arrow keys — move focus between swatches
- Space / Enter — toggle selection
- Home / End — jump to first or last swatch
Listening to changes
Use onValueChange to react to selection changes:
tsx
import { ColorSwatchGroup } from "@urcolor/react";
const onSelectionChange = (value: string[]) => {
console.log("selected:", value);
};
<ColorSwatchGroup.Root
value={selected}
onValueChange={onSelectionChange}
type="single"
>
{/* ... */}
</ColorSwatchGroup.Root>