ColorArea
A 2D color picker that allows users to select colors from a gradient area
Usage
<script setup lang="ts">
import { ColorArea } from '@hareui/vue'
</script><script setup lang="ts">import { ColorArea } from '@hareui/vue'</script><template> <ColorArea default-value="rgb(116, 52, 255)" /></template>Anatomy
ColorArea renders the gradient area with its draggable thumb built in (HeroUI's ColorArea.Thumb child). Style the parts with class and ui.
<template>
<ColorArea />
</template>ui key | data-slot | Element |
|---|---|---|
base | color-area | <div role="application"> gradient area |
thumb | color-area-thumb | <div role="slider"> thumb |
Examples
With Dots
<script setup lang="ts">import { ColorArea } from '@hareui/vue'</script><template> <ColorArea show-dots default-value="hsl(200, 100%, 50%)" /></template>Color Space & Channels
Use color-space to set the color space (RGB, HSL, HSB) and x-channel / y-channel to choose which channels are shown on each axis.
Color Space
X Axis
Y Axis
hsb(219, 58%, 93%)<script setup lang="ts">import type { ColorChannel, ColorSpace, ListBoxItem, SelectValue } from '@hareui/vue'import { ColorArea, colorToString, parseColor, Select } from '@hareui/vue'import { computed, ref } from 'vue'const colorSpaces: ListBoxItem[] = [ { value: 'rgb', label: 'RGB' }, { value: 'hsl', label: 'HSL' }, { value: 'hsb', label: 'HSB' },]const channelsBySpace: Record<ColorSpace, Array<{ value: ColorChannel, label: string }>> = { hsb: [ { value: 'hue', label: 'Hue' }, { value: 'saturation', label: 'Saturation' }, { value: 'brightness', label: 'Brightness' }, ], hsl: [ { value: 'hue', label: 'Hue' }, { value: 'saturation', label: 'Saturation' }, { value: 'lightness', label: 'Lightness' }, ], rgb: [ { value: 'red', label: 'Red' }, { value: 'green', label: 'Green' }, { value: 'blue', label: 'Blue' }, ],}const colorSpace = ref<ColorSpace>('hsb')const color = ref(parseColor('hsb(219, 58%, 93%)'))const xChannel = ref<ColorChannel>('saturation')const yChannel = ref<ColorChannel>('brightness')function onSpaceChange(space: SelectValue | undefined) { if (!space || typeof space !== 'string') return colorSpace.value = space as ColorSpace // Reset the channels to sensible defaults for the new colour space if (space === 'rgb') { xChannel.value = 'blue' yChannel.value = 'green' } else if (space === 'hsl') { xChannel.value = 'saturation' yChannel.value = 'lightness' } else { xChannel.value = 'saturation' yChannel.value = 'brightness' }}// A channel can't be on both axes, so each axis hides the other's channelconst xChannelOptions = computed(() => channelsBySpace[colorSpace.value].filter(c => c.value !== yChannel.value))const yChannelOptions = computed(() => channelsBySpace[colorSpace.value].filter(c => c.value !== xChannel.value))</script><template> <div class="flex flex-col items-center gap-6"> <div class="flex gap-4"> <Select class="w-32" label="Color Space" :model-value="colorSpace" :items="colorSpaces" @update:model-value="onSpaceChange" /> <Select v-model="xChannel" class="w-36" label="X Axis" :items="xChannelOptions" /> <Select v-model="yChannel" class="w-36" label="Y Axis" :items="yChannelOptions" /> </div> <ColorArea v-model="color" :color-space="colorSpace" :x-channel="xChannel" :y-channel="yChannel" /> <div class="flex items-center gap-3"> <div class="size-8 rounded-md border border-default" :style="{ backgroundColor: colorToString(color, 'rgb') }" /> <code class="rounded bg-default/50 px-2 py-1 text-sm"> {{ colorToString(color, colorSpace) }} </code> </div> </div></template>Disabled
<script setup lang="ts">import { ColorArea } from '@hareui/vue'</script><template> <ColorArea disabled default-value="hsl(200, 100%, 50%)" /></template>Controlled
Bind the value with v-model. The committed value is always a Color object; use colorToString(color, 'hex') to read it as a string. Inside a ColorPicker the area reads and writes the picker's colour instead.
Current color: #9b80ff
<script setup lang="ts">import { ColorArea, ColorSwatch, colorToString, parseColor } from '@hareui/vue'import { ref } from 'vue'const color = ref(parseColor('#9B80FF'))</script><template> <div class="flex flex-col gap-4"> <ColorArea v-model="color" color-space="rgb" x-channel="red" y-channel="green" /> <div class="flex w-[300px] items-center gap-3"> <ColorSwatch :color="color" size="md" /> <p class="text-sm text-muted"> Current color: <span class="font-medium">{{ color ? colorToString(color, 'hex') : '(empty)' }}</span> </p> </div> </div></template>Render Function
HeroUI's render prop replaces the root element. In Vue, attributes you pass to ColorArea fall through to the root element.
<script setup lang="ts">import { ColorArea } from '@hareui/vue'</script><template> <!-- Attributes fall through to the root element (HeroUI's `render` prop) --> <ColorArea default-value="rgb(116, 52, 255)" data-custom="slider" /></template>Customization
Tailwind CSS
<script setup lang="ts">import { ColorArea } from '@hareui/vue'</script><template> <ColorArea class="size-44 rounded-3xl" :ui="{ thumb: 'size-5 rounded-full border-4 border-white' }" default-value="rgb(116, 52, 255)" /></template>Global Configuration
app.use(createHareUI({
ui: { colorArea: { slots: { base: 'rounded-3xl', thumb: 'size-5 border-4' } } },
}))Styling Reference
Slots
base→[data-slot="color-area"]– gradient area with inner shadowthumb→[data-slot="color-area-thumb"]– draggable thumb
Variants
showDots– adds a dot grid overlay for precision picking
Interactive States
- Disabled:
[data-disabled]on the area and thumb - Focus Visible:
:focus-visibleon the thumb - Dragging:
[data-dragging="true"]on the thumb
API Reference
Props
| Prop | Type | Default | Description |
|---|---|---|---|
modelValue | string | Color | - | The colour value (controlled), as a hex/CSS colour string or a Color object. Use with v-model.
When omitted and used inside a ColorPicker, falls back to its current colour. |
defaultValue | string | Color | - | The default colour when uncontrolled. |
colorSpace | ColorSpace | - | The colour space the channels are read from. Defaults to the colour's own space. |
xChannel | ColorChannel | - | Colour channel for the horizontal (x) axis. Defaults to the first channel of the colour space
that isn't yChannel (hue for HSL/HSB, red for RGB), as in React Aria. |
yChannel | ColorChannel | - | Colour channel for the vertical (y) axis. Defaults to the first channel of the colour space
that isn't the x channel (saturation for HSL/HSB, green for RGB), as in React Aria. |
disabled | boolean | - | Whether the colour area is disabled. |
showDots | boolean | - | Whether to show the dot grid overlay for precision picking. |
xName | string | - | The name of the x channel's hidden input, for form submission. |
yName | string | - | The name of the y channel's hidden input, for form submission. |
ui | ComponentSlots<{ slots: { base: string[]; thumb: string[]; }; variants: { showDots: { false: {}; true: { base: string[]; }; }; }; defaultVariants: { showDots: boolean; }; }> | - | Per-slot class overrides. |
Emits
| Event | Payload | Description |
|---|---|---|
update:modelValue | [value: string | Color | undefined] | - |
change | [value: Color] | - |
changeEnd | [value: Color] | - |
Accessibility
- The area is
role="application"and the thumb arole="slider"with anaria-valuetextdescribing both channels - Keyboard: arrow keys move the thumb on both axes, PageUp/PageDown/Home/End take larger steps



