ColorChooser

View source
A visual color picker component.

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.

Built on Nuxt UI's ColorPicker component

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:

Example error 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
idstring
namestring
format'hex'"hex" | "rgb" | "hsl" | "cmyk" | "lab"

当前激活的颜色格式。

formats["hex"]ColorFormat[]

启用的颜色格式 tab 列表,长度 >= 2 时在 popover 顶部渲染切换器。 仅一项时不渲染 tab,等价于 format

swatchesstring[] | string[][]

预设色板。 一维数组渲染为单行,二维数组渲染为多行分组。

trigger'button'"button" | "chip" | "input"

触发器形态。

  • button:色点 + 色值 / label 的常规按钮(默认)
  • chip:仅一个圆形色点
  • input:色点 leading + 可输入色值文本框
labelstring

trigger='button' 时按钮上的文本,未传则显示当前色值。

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

未选中颜色时占位的图标。

colorPickerPropsOmit<ColorPickerProps, "format" | "disabled" | "modelValue">

透传给 UColorPicker 的属性。 modelValueformatdisabled 由组件内部托管。

mode'click'M

The display mode of the popover.

content{ side: 'bottom', sideOffset: 8, collisionPadding: 8 }PopoverContentProps & Partial<EmitsToProps<PopoverContentImplEmits>>

The content of the popover.

arrowfalseboolean | PopoverArrowProps

Display an arrow alongside the popover. { rounded: true }

portaltruestring | false | true | HTMLElement

Render the popover in a portal.

referenceElement | VirtualElement

The reference (or anchor) element that is being referred to for positioning.

Accepts an element or a virtual element (anything with getBoundingClientRect), and can be changed reactively to re-anchor the popover (e.g. for a guided tour). If not provided will use the current component as anchor.

openDelaynumber

The duration from when the mouse enters the trigger until the hover card opens.

closeDelaynumber

The duration from when the mouse leaves the trigger or content until the hover card closes.

modelValuestring
closeOnSwatchtrueboolean

点击预设色后是否自动关闭弹层。

clearableboolean

是否在底部 actions 区显示清除按钮。

copyableboolean

actions 区显示复制按钮(基于 navigator.clipboard)。

disabledboolean

是否禁用。禁用时弹层不会打开、所有交互失效。

highlightboolean

Highlight the ring color like a focus state.

dismissibletrueboolean

When false, the popover will not close when clicking outside or pressing escape.

modalfalseboolean

The 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.

uiRecord<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

app.config.ts
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'
      }
    }
  }
})

Changelog

No recent changes
Copyright © 2025 - 2026 YiXuan - MIT License