Skip to content

ColorSwatchGroup

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

Preview

Selected: hsl(210, 80%, 50%)

Source code
vue
<script setup lang="ts">
import { ref } from "vue";
import "internationalized-color/css";
import { ColorSwatchGroupRoot, ColorSwatchGroupItem } from "@urcolor/vue";
import { Check } from "lucide-vue-next";

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%)",
];

const selected = ref<string[]>([colors[0]!]);
</script>

<template>
  <div class="flex flex-col gap-4">
    <ColorSwatchGroupRoot
      v-model="selected"
      type="single"
      class="flex items-center gap-2"
    >
      <ColorSwatchGroupItem
        v-for="color in colors"
        :key="color"
        :value="color"
        class="
          flex size-10 cursor-pointer items-center justify-center rounded-lg
          outline-none
        "
      >
        <Check
          class="
            size-5 text-white drop-shadow-[0_1px_2px_rgba(0,0,0,0.5)]
            transition-opacity duration-150
          "
          :class="selected.includes(color) ? 'opacity-100' : 'opacity-0'"
        />
      </ColorSwatchGroupItem>
    </ColorSwatchGroupRoot>
    <p class="text-sm text-gray-500">
      Selected: <code>{{ selected[0] ?? 'none' }}</code>
    </p>
  </div>
</template>

Anatomy

vue
<template>
  <ColorSwatchGroupRoot>
    <ColorSwatchGroupItem />
    <ColorSwatchGroupItem />
    <ColorSwatchGroupItem />
  </ColorSwatchGroupRoot>
</template>

Examples

Single Selection

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

Selected: hsl(210, 80%, 50%)

Source code
vue
<script setup lang="ts">
import { ref } from "vue";
import "internationalized-color/css";
import { ColorSwatchGroupRoot, ColorSwatchGroupItem } from "@urcolor/vue";
import { Check } from "lucide-vue-next";

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%)",
];

const selected = ref<string[]>([colors[0]!]);
</script>

<template>
  <div class="flex flex-col gap-4">
    <ColorSwatchGroupRoot
      v-model="selected"
      type="single"
      class="flex items-center gap-2"
    >
      <ColorSwatchGroupItem
        v-for="color in colors"
        :key="color"
        :value="color"
        class="
          flex size-10 cursor-pointer items-center justify-center rounded-lg
          outline-none
        "
      >
        <Check
          class="
            size-5 text-white drop-shadow-[0_1px_2px_rgba(0,0,0,0.5)]
            transition-opacity duration-150
          "
          :class="selected.includes(color) ? 'opacity-100' : 'opacity-0'"
        />
      </ColorSwatchGroupItem>
    </ColorSwatchGroupRoot>
    <p class="text-sm text-gray-500">
      Selected: <code>{{ selected[0] ?? 'none' }}</code>
    </p>
  </div>
</template>

Multiple Selection

Toggle any number of swatches independently.

Selected: none

Source code
vue
<script setup lang="ts">
import { ref } from "vue";
import "internationalized-color/css";
import { ColorSwatchGroupRoot, ColorSwatchGroupItem } from "@urcolor/vue";
import { Check } from "lucide-vue-next";

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%)",
];

const selected = ref<string[]>([]);
</script>

<template>
  <div class="flex flex-col gap-4">
    <ColorSwatchGroupRoot
      v-model="selected"
      type="multiple"
      class="flex items-center gap-2"
    >
      <ColorSwatchGroupItem
        v-for="color in colors"
        :key="color"
        :value="color"
        class="
          flex size-10 cursor-pointer items-center justify-center rounded-lg
          outline-none
        "
      >
        <Check
          class="
            size-5 text-white drop-shadow-[0_1px_2px_rgba(0,0,0,0.5)]
            transition-opacity duration-150
          "
          :class="selected.includes(color) ? 'opacity-100' : 'opacity-0'"
        />
      </ColorSwatchGroupItem>
    </ColorSwatchGroupRoot>
    <p class="text-sm text-gray-500">
      Selected: <code>{{ selected.length ? selected.join(', ') : 'none' }}</code>
    </p>
  </div>
</template>

API Reference

ColorSwatchGroupRoot

The root container that manages selection state and keyboard navigation.

PropTypeDefaultDescription
type'single' | 'multiple''single'Whether to allow single or multiple selection.
modelValuestring[][]The selected color value(s) (v-model).
defaultValuestring[][]Default selected value(s) when uncontrolled.
disabledbooleanfalseDisables all items in the group.
orientation'horizontal' | 'vertical''horizontal'Orientation for arrow key navigation.
loopbooleantrueWhether keyboard navigation loops around.
rovingFocusbooleantrueWhether to use roving focus for keyboard navigation.
dir'ltr' | 'rtl'Reading direction.
EventPayloadDescription
update:modelValuestring[]Emitted when the selection changes.

ColorSwatchGroupItem

An individual selectable swatch. Renders a ColorSwatchRoot internally.

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.
data-orientation'horizontal' | 'vertical'The group orientation (on root).

Accessibility

ColorSwatchGroup uses toggle-group semantics with roving focus for efficient keyboard navigation across swatch items.

ARIA Labels

AttributeDescription
role="radiogroup"Applied in single selection mode for screen reader recognition.
role="group"Applied in multiple selection mode.
aria-pressedIndicates the toggle state of each item.
data-stateExposes 'on' or 'off' for styling selected/unselected states.

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.