ColorChooser
Introduction
MColorChooser is a visual color picker component. It provides a color wheel and HSL sliders for color selection, supports custom trigger styles, preset swatches, copy and clear actions, and synchronizes the value in one of three formats: hex, rgb, or hsl.
Usage
The default button trigger displays the current color value. Click to open the popover and select a color from the panel to sync with v-model:
<script setup lang="ts">
const value = ref("#0ea5e9")
</script>
<template>
<MColorChooser v-model="value" />
</template>
formats Output Format
Switch between hex, rgb, hsl, cmyk, and lab at the top of the popover — the current value is converted to the selected format:
<script setup lang="ts">
const value = ref('#22c55e')
</script>
<template>
<MColorChooser v-model="value" :formats="['hex', 'rgb', 'hsl', 'cmyk', 'lab']" />
</template>
swatches Single-Group Presets
A one-dimensional swatches array renders as a continuous color palette. Clicking a swatch selects the color and closes the popover by default:
<script setup lang="ts">
const value = ref('#ef4444')
</script>
<template>
<MColorChooser
v-model="value"
:swatches="[
'#ef4444',
'#f97316',
'#f59e0b',
'#eab308',
'#84cc16',
'#22c55e',
'#10b981',
'#14b8a6',
'#06b6d4',
'#0ea5e9',
'#3b82f6',
'#6366f1',
'#8b5cf6',
'#a855f7',
'#d946ef',
'#ec4899'
]"
/>
</template>
swatches Grouped Presets
A two-dimensional swatches array groups colors by row to display hues and neutral tones. closeOnSwatch controls whether the popover closes after selection:
<script setup lang="ts">
const value = ref('#3b82f6')
</script>
<template>
<MColorChooser
v-model="value"
:close-on-swatch="false"
:swatches="[
['#ef4444', '#f97316', '#f59e0b', '#eab308', '#84cc16', '#22c55e', '#10b981', '#14b8a6'],
['#06b6d4', '#0ea5e9', '#3b82f6', '#6366f1', '#8b5cf6', '#a855f7', '#d946ef', '#ec4899'],
['#0a0a0a', '#404040', '#737373', '#a3a3a3', '#d4d4d4', '#e5e5e5', '#f5f5f5', '#ffffff']
]"
/>
</template>
trigger Trigger Style
trigger controls the trigger appearance: button renders a button, chip renders a compact color block only, and input provides a color dot + text input that validates hex on blur:
<script setup lang="ts">
const value = ref("#f59e0b")
</script>
<template>
<MColorChooser v-model="value" trigger="chip" />
</template>
copyable Copy Button
copyable enables a copy button at the bottom of the popover. Clicking it fires the @copy event:
<script setup lang="ts">
const value = ref("#a855f7")
</script>
<template>
<MColorChooser v-model="value" copyable />
</template>
clearable Clear Button
clearable enables a clear button at the bottom of the popover. Clicking it resets the current value and fires the @clear event:
<script setup lang="ts">
const value = ref("#a855f7")
</script>
<template>
<MColorChooser v-model="value" clearable />
</template>
ui Style Customization
ui overrides the class of internal slots to customize the swatch grid and swatch size, without affecting the color picker, copy, or clear mechanisms:
<script setup lang="ts">
const value = ref('#14b8a6')
</script>
<template>
<MColorChooser
v-model="value"
:ui="{
swatches: 'grid grid-cols-4 gap-2',
swatch: 'size-8 rounded-lg ring-2 ring-default cursor-pointer hover:ring-primary'
}"
:swatches="[
'#ef4444',
'#f97316',
'#f59e0b',
'#eab308',
'#84cc16',
'#22c55e',
'#10b981',
'#14b8a6',
'#06b6d4',
'#0ea5e9',
'#3b82f6',
'#6366f1',
'#8b5cf6',
'#a855f7',
'#d946ef',
'#ec4899'
]"
/>
</template>
disabled Disabled State
disabled prevents the popover from opening. The input trigger enters a read-only state while still displaying the current value:
<script setup lang="ts">
const value = ref("#6b7280")
</script>
<template>
<MColorChooser v-model="value" disabled />
</template>
Examples
Inheriting Field Context
When placed inside UFormField, it inherits size and error state, and the trigger renders according to the form state:
<template>
<UFormField label="Brand Color" size="xs" error="Example error state">
<MColorChooser />
</UFormField>
</template>
Inside UFieldGroup
When placed alongside a button inside UFieldGroup, they share size, border-radius, and border connection — ideal for inline color picking within a form:
<template>
<UFieldGroup size="xs">
<MColorChooser trigger="input" />
<UButton icon="i-lucide-pipette" color="neutral" variant="subtle" />
</UFieldGroup>
</template>
Custom Trigger Rendering
The default slot gives full control over the trigger appearance, while open and value slot props keep the popover state accessible:
<script setup lang="ts">
const value = ref('#ec4899')
const tailwindPalette = [
'#ef4444',
'#f97316',
'#f59e0b',
'#eab308',
'#84cc16',
'#22c55e',
'#10b981',
'#14b8a6',
'#06b6d4',
'#0ea5e9',
'#3b82f6',
'#6366f1',
'#8b5cf6',
'#a855f7',
'#d946ef',
'#ec4899'
]
</script>
<template>
<MColorChooser v-model="value" :swatches="tailwindPalette">
<template #default="{ open, value: current }">
<button
type="button"
class="size-12 rounded-xl border-2 border-default cursor-pointer hover:scale-105 transition flex items-center justify-center"
:style="{ backgroundColor: current }"
:aria-expanded="open"
>
<UIcon v-if="!current" name="i-lucide-plus" class="text-muted" />
</button>
</template>
</MColorChooser>
</template>
API
Props
| Prop | Default | Type |
|---|---|---|
id | string | |
name | string | |
format | 'hex' | "hex" | "rgb" | "hsl" | "cmyk" | "lab"当前激活的颜色格式。 |
formats | ["hex"] | ColorFormat[]启用的颜色格式 tab 列表,长度 >= 2 时在 popover 顶部渲染切换器。
仅一项时不渲染 tab,等价于 |
swatches | string[] | string[][]预设色板。 一维数组渲染为单行,二维数组渲染为多行分组。 | |
trigger | 'button' | "button" | "chip" | "input"触发器形态。
|
label | string
| |
placeholder | '选择颜色' | string未选中颜色时的占位文案。 |
size | 'md' | "md" | "xs" | "sm" | "lg" | "xl"触发器尺寸,同时驱动主题 size variant。 |
color | 'neutral' | "primary" | "secondary" | "info" | "success" | "warning" | "error" | "important" | "neutral"触发按钮的颜色(trigger=button|chip 生效)。 |
variant | 'subtle' | "solid" | "outline" | "soft" | "subtle" | "ghost" | "link"触发按钮的视觉变体(trigger=button|chip 生效)。 |
icon | 'i-lucide-palette' | any未选中颜色时占位的图标。 |
colorPickerProps | Omit<ColorPickerProps, "format" | "disabled" | "modelValue">透传给 | |
mode | 'click' | MThe display mode of the popover. |
content | { side: 'bottom', sideOffset: 8, collisionPadding: 8 } | PopoverContentProps & Partial<EmitsToProps<PopoverContentImplEmits>>The content of the popover. |
arrow | false | boolean | PopoverArrowPropsDisplay an arrow alongside the popover.
|
portal | true | string | false | true | HTMLElementRender the popover in a portal. |
reference | Element | VirtualElementThe reference (or anchor) element that is being referred to for positioning. Accepts an element or a virtual element (anything with | |
openDelay | numberThe duration from when the mouse enters the trigger until the hover card opens. | |
closeDelay | numberThe duration from when the mouse leaves the trigger or content until the hover card closes. | |
modelValue | string | |
closeOnSwatch | true | boolean点击预设色后是否自动关闭弹层。 |
clearable | boolean是否在底部 actions 区显示清除按钮。 | |
copyable | booleanactions 区显示复制按钮(基于 navigator.clipboard)。 | |
disabled | boolean是否禁用。禁用时弹层不会打开、所有交互失效。 | |
highlight | booleanHighlight the ring color like a focus state. | |
dismissible | true | booleanWhen |
modal | false | booleanThe modality of the popover. When set to true, interaction with outside elements will be disabled and only popover content will be visible to screen readers. |
ui | Record<string, ClassNameValue> & { content?: SlotClass; arrow?: SlotClass; body?: SlotClass; header?: SlotClass; section?: SlotClass; swatches?: SlotClass; swatch?: SlotClass; actions?: SlotClass; actionsValue?: SlotClass; actionsButtons?: SlotClass; triggerChipWrapper?: SlotClass; triggerChip?: SlotClass; triggerLabel?: SlotClass; triggerIcon?: SlotClass; } |
Emits
| Event | Type |
|---|---|
update:modelValue | [value: string] |
update:open | [open: boolean] |
change | [value: string] |
clear | [] |
copy | [value: string] |
format-change | [format: ColorFormat] |
Slots
| Slot | Type |
|---|---|
default | { open: boolean; value: string; } |
leading | { value: string; } |
trailing | { value: string; } |
swatches | { swatches: string[][]; select: (color: string) => void; } |
actions | { value: string; copy: () => void; clear: () => void; } |
Theme
export default defineAppConfig({
ui: {
colorChooser: {
slots: {
body: 'p-2 flex flex-col gap-2 min-w-[14rem]',
header: 'flex items-center justify-between gap-2',
section: 'flex flex-col gap-1.5',
swatches: 'grid grid-cols-8 gap-1',
swatch: 'rounded ring-1 ring-default cursor-pointer transition hover:scale-110 hover:ring-2 hover:ring-primary aria-selected:ring-2 aria-selected:ring-primary',
actions: 'mt-1 flex items-center justify-between gap-1.5 border-t border-default pt-2',
actionsValue: 'text-xs text-muted tabular-nums truncate',
actionsButtons: 'flex items-center gap-1',
triggerChipWrapper: '',
triggerChip: 'rounded-full ring-1 ring-default shrink-0 transition',
triggerLabel: 'tabular-nums truncate',
triggerIcon: 'text-muted shrink-0'
},
variants: {
size: {
xs: {
triggerChip: 'size-2.5',
swatch: 'size-4',
triggerIcon: 'size-3'
},
sm: {
triggerChip: 'size-3',
swatch: 'size-4',
triggerIcon: 'size-3.5'
},
md: {
triggerChip: 'size-3.5',
swatch: 'size-5',
triggerIcon: 'size-4'
},
lg: {
triggerChip: 'size-4',
swatch: 'size-5',
triggerIcon: 'size-4'
},
xl: {
triggerChip: 'size-5',
swatch: 'size-6',
triggerIcon: 'size-5'
}
},
trigger: {
button: {},
chip: {},
input: {}
},
disabled: {
true: {
body: 'opacity-60 pointer-events-none'
}
}
},
defaultVariants: {
size: 'md',
trigger: 'button'
}
}
}
})