Alpha
These components are built on top of Reka UI Color components, which are still in alpha. APIs may change in future releases.
<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>