生态产品多个设计器现已发布候选版本

查看生态总览
跳转到内容

ColorSwatchPicker 颜色色块选择器 ​

从一组固定颜色中选择一个:主题色、标签色、高亮色。每格是一个 role=radio 的色块,整组是一个 radiogroup,即把选项绘制为颜色的单选组。需要自由调出任意颜色时使用颜色选择器,它内嵌的预设色板使用的就是本组件。

用法 ​

提供一组颜色数据即自动铺开;每格是一个 radio,方向键在格子间移动并选中

主题色
当前:#3b82f6

组件结构 ​

加粗的是必需部件。

data-scope="color-swatch-picker":root · label · item · swatch · indicator · hidden-input

示例 ​

手写格子 ​

不提供数据也可以:每格自行声明 value,名字与禁用写在格子上;半透明颜色铺在棋盘格上

高亮色
当前:rgb(225, 29, 72)

状态 ​

禁用整组置灰、只读只阻止落值不阻止焦点、无效把描边转为警示色

禁用
只读
必填未选

尺寸与语气 ​

格子边长跟随控件行高分三档;tone 决定选中描边与选中徽标使用哪族颜色

sm
md
lg
brand
neutral
success
warning
danger
info

设计指引 ​

何时使用 ​

  • 可选颜色是有限的一组,且每个颜色都有含义(品牌色、状态色、日历分类色)。
  • 需要一次看到全部选项再挑选,不打开浮层。
  • 表单需要提交一个颜色串,且不需要自由调色。

何时不用 ​

  • 需要自由调出任意颜色时,使用颜色选择器,它把色板与取色面组合在一起。
  • 只展示一个颜色、不接受选择时,使用颜色色块。
  • 需要手动输入颜色串时,使用颜色字段。
  • 选项不是颜色而是文字时,使用单选组。

特性 ​

  • 选中按颜色比较而不按字符串比较:rgb(255, 0, 0) 与 #ff0000 是同一格,受控 value 使用任一写法都能匹配。
  • 与单选组同一套 roving tabindex:整组只占一个 Tab 位,四个方向键在格子间移动焦点并选中,到末端回绕,禁用格跳过;Space 选中当前格。
  • 焦点从组外进入时落在已选中的格子,没有选中时落在第一格。
  • swatches 提供数据:可访问名称与禁用从数据中读取,格子部件只需报告 value;不写默认内容时按数据自动铺开。
  • 每格的色块面由 Swatch 家族绘制:无法解析的串只显示棋盘格,半透明颜色铺在棋盘格上。
  • readOnly 时方向键照常移动焦点但不取值;disabled 整组置灰,格子仍可聚焦。
  • 尺寸 sm / md / lg 三档:格子边长跟随控件行高,与旁边的按钮、字段等高。
  • 选中徽标与色块的品牌描边随 data-tone 变化;徽标自带一圈画布色描边,落在任何颜色上都可见。
  • 高对比模式下色块保留原色,选中描边与徽标换用系统高亮色;打印时徽标改为实边。
  • 不写结构时按标准结构铺出整套部件(Vue 不写默认插槽、React 不给 children、Web Components 不写部件或只写 root),三端出自同一个结构函数:label 给标题文字,接着是 swatches 的每一格,格里依次是表单影子、色块面与选中标记;没给 swatches 时只有标题。Web Components 里标题可以写在宿主的直接子节点 slot="label" 上接管。

组合 ​

  • 内嵌在颜色选择器的浮层中作为预设色板。
  • 与颜色字段并排:色板选择常用色,字段输入精确值。
  • 放入表单字段承接标题、说明与错误信息,disabled / readOnly / invalid / required 随字段下发。

最佳实践 ​

  • 每格提供名称(swatches[].label 或部件的 label),读屏用户听到的应是“品牌红”而不是 #e11d48。
  • 色板的颜色数量控制在一眼可扫完的范围,更多时改用颜色选择器。
  • 有初始值时使用 defaultValue,焦点进组时直接落在该格。

反模式 ​

  • 用它做多选:一格只能选中一个;需要多选颜色时使用复选框组配合颜色色块。
  • 把颜色写成 red 等关键字:不在支持的写法内,该格只显示棋盘格。
  • 同一组内放同一颜色的不同写法:它们会同时算作选中。

API 参考 ​

产物 ​

层值
自定义元素<xh-color-swatch-picker>
Vue 组件XhColorSwatchPickerItem XhColorSwatchPickerLabel XhColorSwatchPickerRoot
组合式函数useColorSwatchPicker
状态机colorSwatchPickerMachine
皮肤@xihan-ui/styles/color-swatch-picker.css

Props ​

属性类型必填说明
swatchesColorSwatchPickerNode[]格子数据,可及名与禁用的事实源。提供后格子部件只需声明 value。 未提供时回到名字与禁用都写在格子部件上的方式。
valuestring | null选中的颜色串。提供即受控:写入只发 onValueChange 不落内部值。写法不同的同一颜色也视为选中。
defaultValuestring | null
disabledboolean
readOnlyboolean只读:不可选择,但仍可聚焦、方向键照常移动焦点,对比度不降低。
invalidboolean校验失败:只改变呈现,不阻止交互。
requiredboolean必填:随表单校验一起使用,只发无障碍属性,不自行拦截提交。
dirDirection文字方向,默认 'ltr';只改写左右两键的语义。
namestring表单字段名。
sizeSize尺寸:sm / md / lg,影响格子的边长与间距。
toneTone语气:决定选中环与选中标记使用哪族颜色。
translationsPartial<ColorSwatchPickerTranslations>
labelledboolean作者渲染了 label 部件时置真,由适配器统计而不是判断标题文字是否有值。 为假时根不输出 aria-labelledby,名字交给 aria-label 的文案:指向未渲染的 id 是悬空引用。
ariaLabelstring不显示出来的名字,写成 aria-label 落在与字段标签同一个部件上。 放进字段(字段接线)或渲染了 label 部件时名字以它们为准,aria-labelledby 盖过它。
onValueChange(details: ColorSwatchPickerValueChangeDetails) => voidvalue 变化回调。

ColorSwatchPickerNode ​

swatches 的元素。

字段类型必填说明
valuestring是颜色串,也是该格的身份。
labelstring读屏朗读该格的方式,例如「品牌红」;默认朗读颜色串。
disabledboolean该格禁用:方向键跳过它,但它仍可聚焦、仍是导航起点。

事件 ​

自定义元素将载荷放在 detail;Vue 使用同名 emit。

事件载荷说明
value-changeColorSwatchPickerValueChangeDetails选中值变化;detail 为 { value: string | null }

插槽 ​

仅列出带载荷的插槽。

Vue 组件插槽载荷说明
XhColorSwatchPickerRootdefaultColorSwatchPickerRootSlotProps
XhColorSwatchPickerRootlabel—

React 适配器 props ​

只列各组件自己声明的那些:继承自 ComponentPropsWithRef 的 DOM 属性不在其中,根组件上与上面 Props 表同名的也不重复列。Vue 的对应物是上面的插槽表。

React 组件属性类型必填说明
XhColorSwatchPickerItemvaluestring是
XhColorSwatchPickerItemlabelstring读屏朗读该格子的方式;默认交给 connect 查询 swatches,都没有时朗读颜色串。
XhColorSwatchPickerItemdisabledboolean默认交给 connect 查询 swatches,写死 false 会覆盖数据中的禁用。
XhColorSwatchPickerRootlabelReactNode标题文字。提供后不必再写 label 部件。
XhColorSwatchPickerRootchildrenSlotChildren<ColorSwatchPickerRootSlotProps>

状态 ​

公开状态写入 data-state。

部件取值
item'checked' | 'unchecked'
swatch'checked' | 'unchecked'
indicator'checked' | 'unchecked'
hidden-input'checked' | 'unchecked'

以下名称仅用于内部状态机。

状态:idle

事件:VALUE.SET · ITEM.SELECT · ITEM.FOCUS · GROUP.BLUR · FORM.RESET · PRESS.START · PRESS.END

判据:canPress

connect API ​

getXxxProps() 返回对应部件的宿主属性。

成员类型说明
valuestring | null
swatchesreadonly ColorSwatchPickerNodeMeta[]由 swatches 推导的格子元信息,按数据顺序排列;未提供 swatches 时为空数组。
focusedValuestring | null焦点在组外时为 null。
isSelected(value: string) => boolean某个颜色串是否为当前选中的格:写法不同(#f00 与 rgb(255,0,0))也视为同一颜色。
setValue(next: string | null) => void
getRootProps() => T['element']
getLabelProps() => T['element']
getItemProps(props: ColorSwatchPickerItemProps) => T['element']一格:role=radio,颜色串是它的身份。
getSwatchProps(props: ColorSwatchPickerItemProps) => T['element']格内的色块面:使用 Swatch 家族绘制颜色,纯装饰。
getIndicatorProps(props: ColorSwatchPickerItemProps) => T['element']选中标记(对号),纯装饰。
getHiddenInputProps(props: ColorSwatchPickerItemProps) => T['input']格子对应的隐藏原生 radio 输入,用于表单提交。

无障碍 ​

键盘 ​

规格出处:W3C APG

按键生效条件行为
Tab / Shift+Tabfocus outside the group整组只占一个 Tab 位:焦点进入锚点格子(即选中的那格);落到容器上时由容器转投锚点格子,锚点缺席或被禁用才落首个可停留格
ArrowDown / ArrowRightfocus in group, group not disabled焦点移到下一个可停留格并选中,末格回绕到首格;dir=rtl 时改由 ArrowLeft 承担
ArrowUp / ArrowLeftfocus in group, group not disabled焦点移到上一个可停留格并选中,首格回绕到末格;dir=rtl 时改由 ArrowRight 承担
Spacefocus on item, item not disabled选中当前格
Spaceheld on item, 格子未禁用且组未禁用、非只读按住期间该格投影 data-pressed,与指针 :active 同一副按压面(换描边并缩放);抬起或失焦撤下,按住途中整组转入禁用或只读也撤下。Enter 不是 radio 的激活键,按住它没有按压面

ARIA ​

以下属性由 connect 生成。

部件属性值
rootaria-invalid'true' | 'false'
rootaria-labelprops.ariaLabel
rootaria-labelledbylabel 部件的 id | undefined
rootaria-readonly'true' | 'false'
rootaria-required'true' | 'false'
rootrole'radiogroup'
itemaria-checked'true' | 'false'
itemaria-disabled'true' | 'false'
itemaria-labelitemLabel(item)
itemrole'radio'
swatcharia-hidden'true'
indicatoraria-hidden'true'
hidden-inputaria-hidden'true'
  • 根是 role=radiogroup,名称取 label 部件,未提供时读 translations.group;label 部件真渲染了时才输出 aria-labelledby。Vue / React 的 label 属性不论手写格子还是数据驱动都会铺出标题。
  • 直接放进表单字段时,字段的标题并进组名、说明进描述链。
  • 每格是 role=radio 并显式输出 aria-checked;名称依次取 label、swatches 中的 label、translations.swatch(value),颜色串无法表达含义时务必提供名称。
  • 禁用格用 aria-disabled 表达,仍可聚焦,仍是方向键的起点。
  • 每格内有一个 inert 的隐藏原生 radio 承接表单提交,不进入焦点序列与可访问树。

样式参考 ​

皮肤 ​

@xihan-ui/styles/color-swatch-picker.css 按 [data-scope="color-swatch-picker"][data-part="root"] 部件选择器书写,发布产物以挂载类 .xh-scope-color-swatch-picker 代替其中的 data-scope(特异性相同),位于 xihan.components 层。覆盖样式使用 xihan.overrides,部件选择器照常可用。

forced-colors: active 下另有一套规则:颜色交给系统,边框与状态标记改用系统色关键字。

数据属性 ​

由 connect 生成;条件不成立时不输出无值属性。

部件属性值
rootdata-disabled''(条件成立时才出现)
rootdata-invalid''(条件成立时才出现)
rootdata-readonly''(条件成立时才出现)
rootdata-required''(条件成立时才出现)
rootdata-sizeprops.size
rootdata-toneprops.tone
labeldata-disabled''(条件成立时才出现)
itemdata-disabled''(条件成立时才出现)
itemdata-invalid''(条件成立时才出现)
itemdata-pressed''(条件成立时才出现)
itemdata-readonly''(条件成立时才出现)
itemdata-state'checked' | 'unchecked'
swatchdata-disabled''(条件成立时才出现)
swatchdata-invalid''(条件成立时才出现)
swatchdata-readonly''(条件成立时才出现)
swatchdata-state'checked' | 'unchecked'
swatchdata-xh-swatch''
swatchdata-xh-swatch-sizeprops.size
indicatordata-disabled''(条件成立时才出现)
indicatordata-invalid''(条件成立时才出现)
indicatordata-readonly''(条件成立时才出现)
indicatordata-state'checked' | 'unchecked'
hidden-inputdata-disabled''(条件成立时才出现)
hidden-inputdata-invalid''(条件成立时才出现)
hidden-inputdata-readonly''(条件成立时才出现)
hidden-inputdata-state'checked' | 'unchecked'

CSS 变量 ​

本组件公开覆盖槽由独立皮肤的实际消费位生成;默认来源、作用部件和状态均与 CSS 同源。

变量部件CSS 属性状态默认来源说明
--xh-color-swatch-picker-gaplabel
root
gap
margin-block-end
default--xh-_color-swatch-picker-gapcolor-swatch-picker 的 label、root 部件 gap、margin-block-end 覆盖槽。
--xh-color-swatch-picker-icon-sizeroot--xh-icon-sizedefault--xh-_color-swatch-picker-markcolor-swatch-picker 的 root 部件 --xh-icon-size 覆盖槽。
--xh-color-swatch-picker-indicator-bgindicatorbackgrounddefault--xh-_color-swatch-picker-accentcolor-swatch-picker 的 indicator 部件 background 覆盖槽。
--xh-color-swatch-picker-indicator-borderindicatorborderdefault--xh-bg-canvascolor-swatch-picker 的 indicator 部件 border 覆盖槽。
--xh-color-swatch-picker-indicator-fgindicatorbackground-color
color
default
empty
--xh-_tone-oncolor-swatch-picker 的 indicator 部件 background-color、color 覆盖槽。
--xh-color-swatch-picker-indicator-sizeindicatorblock-size
inline-size
default--xh-_color-swatch-picker-indicatorcolor-swatch-picker 的 indicator 部件 block-size、inline-size 覆盖槽。
--xh-color-swatch-picker-item-radiusitem
swatch
--xh-swatch-radius
border-radius
default--xh-shape-insetcolor-swatch-picker 的 item、swatch 部件 --xh-swatch-radius、border-radius 覆盖槽。
--xh-color-swatch-picker-item-sizeitemblock-size
inline-size
default--xh-_color-swatch-picker-cellcolor-swatch-picker 的 item 部件 block-size、inline-size 覆盖槽。
--xh-color-swatch-picker-label-fglabelcolordefault--xh-fg-mutedcolor-swatch-picker 的 label 部件 color 覆盖槽。
--xh-color-swatch-picker-label-fg-disabledlabelcolordisabled--xh-fg-mutedcolor-swatch-picker 的 label 部件 color 覆盖槽。
--xh-color-swatch-picker-label-font-sizelabelfont-sizedefault--xh-text-label-sizecolor-swatch-picker 的 label 部件 font-size 覆盖槽。
--xh-color-swatch-picker-label-font-weightlabelfont-weightdefault--xh-text-label-weightcolor-swatch-picker 的 label 部件 font-weight 覆盖槽。
--xh-color-swatch-picker-label-gaplabelmargin-block-enddefault--xh-space-2color-swatch-picker 的 label 部件 margin-block-end 覆盖槽。
--xh-color-swatch-picker-ringitem
swatch
--xh-swatch-borderstate=checked--xh-_tonecolor-swatch-picker 的 item、swatch 部件 --xh-swatch-border 覆盖槽。
--xh-color-swatch-picker-swatch-borderitem
swatch
--xh-swatch-borderdefault--xh-border-defaultcolor-swatch-picker 的 item、swatch 部件 --xh-swatch-border 覆盖槽。
--xh-color-swatch-picker-swatch-border-hoveritem
swatch
--xh-swatch-borderdisabled
hover
not([data-disabled], [data-readonly], [data-state='checked'])
readonly
state=checked
--xh-border-control-hovercolor-swatch-picker 的 item、swatch 部件 --xh-swatch-border 覆盖槽。
--xh-color-swatch-picker-swatch-border-invalidswatch--xh-swatch-borderinvalid--xh-border-invalidcolor-swatch-picker 的 swatch 部件 --xh-swatch-border 覆盖槽。
--xh-color-swatch-picker-swatch-border-presseditem
swatch
--xh-swatch-borderdisabled
is(:active, [data-pressed])
not([data-disabled], [data-readonly])
pressed
readonly
--xh-_tonecolor-swatch-picker 的 item、swatch 部件 --xh-swatch-border 覆盖槽。

动效 ​

动效角色:按压 · 状态 · 切换(见动效规范)。

border-color · opacity · scale 走 transition 过渡。时长与缓动读动效令牌,改令牌即改全局节奏。

系统开启减弱动效时由令牌层统一收敛,皮肤不另作判断。

响应式 ​

  • 格子排成可换行的网格,一行放不下时换行;粗指针下命中区是整格。

RTL ​

  • dir="rtl" 只对调左右方向键的语义,上下键不受影响;格子的排列顺序由文档方向决定。

Released under The MIT License