Skip to content

ColorSwatchGroup

A group of color swatches with toggle-group selection behavior. Supports single and multiple selection modes with full keyboard navigation.

Preview

Source 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 ColorSwatchGroupBasic() {
  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-gray-500">
        Selected: <code>{selected[0] ?? "none"}</code>
      </p>
    </div>
  );
}

Anatomy

tsx
<ColorSwatchGroup.Root>
  <ColorSwatchGroup.Item />
  <ColorSwatchGroup.Item />
  <ColorSwatchGroup.Item />
</ColorSwatchGroup.Root>

Examples

Single Selection

Click a swatch to select it. Clicking the selected swatch deselects it.

Source 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 ColorSwatchGroupBasic() {
  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-gray-500">
        Selected: <code>{selected[0] ?? "none"}</code>
      </p>
    </div>
  );
}

Multiple Selection

Toggle any number of swatches independently.

Source 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 ColorSwatchGroupMultiple() {
  const [selected, setSelected] = useState<string[]>([]);

  return (
    <div className="flex flex-col gap-4">
      <ColorSwatchGroup.Root
        value={selected}
        onValueChange={setSelected}
        type="multiple"
        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-gray-500">
        Selected: <code>{selected.length ? selected.join(", ") : "none"}</code>
      </p>
    </div>
  );
}

API Reference

ColorSwatchGroup.Root

The root container that manages selection state and keyboard navigation.

PropTypeDefaultDescription
type'single' | 'multiple''single'Whether to allow single or multiple selection.
valuestring[][]The selected color value(s).
defaultValuestring[][]Default selected value(s) when uncontrolled.
disabledbooleanfalseDisables all items in the group.
orientation'horizontal' | 'vertical''horizontal'Orientation for arrow key navigation.
onValueChange(value: string[]) => voidCalled when the selection changes.

ColorSwatchGroup.Item

An individual selectable swatch.

PropTypeDefaultDescription
valuestringrequiredThe color value. Serves as both the selection key and the displayed color.
disabledbooleanfalseDisables this item.
checkerSizenumber16The checkerboard tile size in pixels.
alphabooleanfalseWhen true, reflects the color's alpha channel.

Data Attributes

AttributeValuesDescription
data-state'on' | 'off'Whether the item is selected.
data-disabledPresent when disabledWhether the item is disabled.

Accessibility

ColorSwatchGroup uses toggle-group semantics with keyboard navigation across swatch items.

Keyboard Navigation

KeyAction
TabFocus the selected item (or first if none selected).
Arrow Left / RightMove focus between items (horizontal).
Arrow Up / DownMove focus between items (vertical).
Home / EndMove focus to first / last item.
Space / EnterToggle the focused item.