GitHubX

Color

PreviousNext

A composable set of color components with area picker, sliders, fields, swatches, and eye dropper support.

      
      <script setup lang="ts">
import {
  ColorArea,
  ColorEyeDropper,
  ColorField,
  ColorFieldInput,
  ColorHsbField,
  ColorHslField,
  ColorPicker,
  ColorPickerContent,
  ColorPickerTrigger,
  ColorRgbField,
  ColorSlider,
  ColorSpaceSelect,
  ColorSwatch,
  ColorSwatchPicker,
  ColorSwatchPickerItem,
} from "@/components/color";
import { InputGroupAddon, InputGroupButton } from "@/components/ui/input-group";
import { Separator } from "@/components/ui/separator";
import { type Color, type ColorSpace, colorToString, normalizeColor } from "reka-ui";
import { computed, ref } from "vue";

const history = ref<string[]>(["#56d799", "#fa5454", "#feb400"]);

const colorSpace = ref<ColorSpace | "hex">("hsl");
const currentColor = ref<Color>(normalizeColor("#56d799"));
const hexColor = computed(() => colorToString(currentColor.value, "hex"));
</script>

<template>
  <div class="flex flex-col gap-4">
    <ColorPicker>
      <ColorField :model-value="hexColor" @update:color="currentColor = $event">
        <InputGroupAddon>
          <InputGroupButton size="icon-xs">
            <ColorPickerTrigger>
              <ColorSwatch :color="hexColor" />
            </ColorPickerTrigger>
          </InputGroupButton>
        </InputGroupAddon>
        <ColorFieldInput />
      </ColorField>

      <ColorPickerContent class="p-2" side="bottom" align="start" :side-offset="12">
        <div class="flex items-center justify-between mb-4">
          <div class="flex items-center gap-2">
            <ColorSwatch :color="hexColor" class="size-10 rounded-md" />
            <div class="flex flex-col">
              <span class="text-sm font-medium">Color</span>
              <span class="text-xs text-muted-foreground">{{ hexColor }}</span>
            </div>
          </div>

          <ColorEyeDropper v-model:color="currentColor" />
        </div>

        <ColorArea
          :model-value="currentColor"
          color-space="hsl"
          x-channel="saturation"
          y-channel="lightness"
          class="w-full h-[150px] rounded-md"
          @update:color="currentColor = $event"
        />

        <div class="text-xs text-muted-foreground mt-4">Hue</div>
        <ColorSlider channel="hue" :model-value="currentColor" class="w-full h-4 rounded-md" @update:color="currentColor = $event" />

        <div class="text-xs text-muted-foreground mt-2">Alpha</div>
        <ColorSlider channel="alpha" :model-value="currentColor" class="w-full h-4 rounded-md" @update:color="currentColor = $event" />

        <div class="flex items-center justify-between gap-2 mt-4">
          <ColorSpaceSelect v-model="colorSpace" size="sm" class="flex-none" />

          <ColorHslField v-if="colorSpace === 'hsl'" v-model="currentColor" size="sm" class="flex-none" />
          <ColorHsbField v-if="colorSpace === 'hsb'" v-model="currentColor" size="sm" class="flex-none" />
          <ColorRgbField v-if="colorSpace === 'rgb'" v-model="currentColor" size="sm" class="flex-none" />
        </div>

        <Separator orientation="horizontal" class="my-2 data-[orientation=horizontal]:bg-accent" />

        <div class="flex flex-col gap-2">
          <div class="text-xs text-muted-foreground">Last colors</div>
          <ColorSwatchPicker :model-value="hexColor" class="flex flex-wrap gap-1" @update:model-value="currentColor = normalizeColor($event as string)">
            <ColorSwatchPickerItem v-for="(color, index) in history" :key="index" :value="color" />
          </ColorSwatchPicker>
        </div>
      </ColorPickerContent>
    </ColorPicker>
  </div>
</template>
    

Features

  • Color Area — 2D saturation/lightness picker with customizable channels
  • Color Slider — Single-channel slider for hue, alpha, or other channels
  • Color Fields — HSL, HSB, RGB, and Hex input fields with size variants
  • Color Space Select — Switch between color formats (HSL, HSB, RGB, Hex)
  • Color Swatch Picker — Preset color selection with swatch items
  • Eye Dropper — Native browser color picker support (when available)
  • Composable — Combine components to build custom color pickers

Installation

Install from the Vuzeno registry with the shadcn-vue CLI:

      
      bunx --bun shadcn-vue@latest add https://vuzeno.com/r/color.json
    

Examples

Color Area

Standalone color area with hue slider:

      
      <script setup lang="ts">
import { ColorArea, ColorSlider } from "@/components/color";
import { type Color, normalizeColor } from "reka-ui";
import { ref } from "vue";

const currentColor = ref<Color>(normalizeColor("#56d799"));
</script>

<template>
  <div class="flex flex-col gap-4 w-fit">
    <ColorArea
      :model-value="currentColor"
      color-space="hsl"
      x-channel="saturation"
      y-channel="lightness"
      class="w-[200px] h-[200px] rounded-md"
      @update:color="currentColor = $event"
    />
    <div class="text-xs text-muted-foreground">Hue</div>
    <ColorSlider channel="hue" :model-value="currentColor" class="w-[200px]" @update:color="currentColor = $event" />
  </div>
</template>
    

Color Fields

HSL, HSB, RGB, and Hex fields with ColorSpaceSelect. All field components support sm, default, and lg size variants:

      
      <script setup lang="ts">
import { ColorHexField, ColorHsbField, ColorHslField, ColorRgbField, ColorSpaceSelect } from "@/components/color";
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field";
import { type Color, type ColorSpace, colorToString, normalizeColor } from "reka-ui";
import { computed, ref } from "vue";

const colorSpace = ref<ColorSpace | "hex">("hsl");
const currentColor = ref<Color>(normalizeColor("#56d799"));
const hexColor = computed({
  get: () => colorToString(currentColor.value, "hex"),
  set: (v) => {
    currentColor.value = normalizeColor(v);
  },
});

const fieldOptions = [
  { value: "hsl" as const, label: "HSL" },
  { value: "hsb" as const, label: "HSB" },
  { value: "rgb" as const, label: "RGB" },
  { value: "hex" as const, label: "Hex" },
];
</script>

<template>
  <div class="flex flex-col gap-8">
    <div class="flex flex-col gap-2">
      <FieldLabel>Format</FieldLabel>
      <div class="flex items-center gap-2 flex-wrap">
        <ColorSpaceSelect v-model="colorSpace" :options="fieldOptions" />
        <ColorHslField v-if="colorSpace === 'hsl'" v-model="currentColor" />
        <ColorHsbField v-if="colorSpace === 'hsb'" v-model="currentColor" />
        <ColorRgbField v-if="colorSpace === 'rgb'" v-model="currentColor" />
        <ColorHexField v-if="colorSpace === 'hex'" v-model="hexColor" />
      </div>
    </div>

    <FieldGroup class="w-fit">
      <Field>
        <FieldLabel>Small</FieldLabel>
        <div class="flex items-center gap-2">
          <ColorSpaceSelect v-model="colorSpace" :options="fieldOptions" size="sm" />
          <ColorHslField v-if="colorSpace === 'hsl'" v-model="currentColor" size="sm" />
          <ColorHsbField v-if="colorSpace === 'hsb'" v-model="currentColor" size="sm" />
          <ColorRgbField v-if="colorSpace === 'rgb'" v-model="currentColor" size="sm" />
          <ColorHexField v-if="colorSpace === 'hex'" v-model="hexColor" size="sm" />
        </div>
      </Field>

      <Field>
        <FieldLabel>Default</FieldLabel>
        <div class="flex items-center gap-2">
          <ColorSpaceSelect v-model="colorSpace" :options="fieldOptions" />
          <ColorHslField v-if="colorSpace === 'hsl'" v-model="currentColor" />
          <ColorHsbField v-if="colorSpace === 'hsb'" v-model="currentColor" />
          <ColorRgbField v-if="colorSpace === 'rgb'" v-model="currentColor" />
          <ColorHexField v-if="colorSpace === 'hex'" v-model="hexColor" />
        </div>
      </Field>

      <Field>
        <FieldLabel>Large</FieldLabel>
        <div class="flex items-center gap-2">
          <ColorSpaceSelect v-model="colorSpace" :options="fieldOptions" size="lg" />
          <ColorHslField v-if="colorSpace === 'hsl'" v-model="currentColor" size="lg" />
          <ColorHsbField v-if="colorSpace === 'hsb'" v-model="currentColor" size="lg" />
          <ColorRgbField v-if="colorSpace === 'rgb'" v-model="currentColor" size="lg" />
          <ColorHexField v-if="colorSpace === 'hex'" v-model="hexColor" size="lg" />
        </div>
      </Field>
    </FieldGroup>
  </div>
</template>
    

Color Swatch Picker

Preset color selection with ColorSwatchPicker and ColorSwatchPickerItem:

      
      <script setup lang="ts">
import { ColorSwatch, ColorSwatchPicker, ColorSwatchPickerItem } from "@/components/color";
import { ref } from "vue";

const presetColors = ["#56d799", "#fa5454", "#feb400", "#3b82f6", "#8b5cf6", "#ec4899", "#14b8a6", "#f97316"];

const selectedColor = ref<string>(presetColors[0]!);
</script>

<template>
  <div class="flex flex-col gap-4">
    <div class="flex items-center gap-2">
      <ColorSwatch :color="selectedColor" class="size-8 rounded-md" />
      <span class="text-sm text-muted-foreground">{{ selectedColor }}</span>
    </div>
    <ColorSwatchPicker
      :model-value="selectedColor"
      class="flex flex-wrap gap-2"
      @update:model-value="selectedColor = ($event as string)"
    >
      <ColorSwatchPickerItem v-for="(color, index) in presetColors" :key="index" :value="color" />
    </ColorSwatchPicker>
  </div>
</template>