Button Skills
本说明给 AI 使用。目标是快速判断该用哪个按钮、参数怎么传、哪些样式可以通过 className 定制、哪些属于组件内强约束。
按钮体系定义
这里的“按钮”不是只指传统 button 标签,而是指一组同源的可点击交互控件。
设计原则:
- 网站按钮在架构上同源。
- 基础交互、尺寸、圆角、间距、状态处理尽量统一。
- 不同业务语义主要通过自定义样式区分,而不是无限新增新组件。
- 能用同一个基座按钮派生的类型,优先视为同一按钮体系,而不是独立组件种类。
对 AI 的要求:
- 优先从现有按钮基座中选变体,不要轻易新造按钮组件。
- 语义差异优先通过
className、variant、宽度、边框、hover、颜色来表达。 - 只有在交互结构明显不同的情况下,才切换到另一类按钮基座。
按钮类型覆盖表
| 按钮类型 | 是否纳入体系 | 当前推荐基座/组件 | 说明 |
|---|---|---|---|
| 普通点击按钮 | ✅ | XButton single / GradientButton(onClick) | 最常用操作按钮 |
| 链接按钮 | ✅ | GradientButton(href) | 外观是按钮,行为是跳转 |
| 图标按钮 | ✅ | XButton single | 传图标,text: '' |
| 文字按钮 | ✅ | XButton single / GradientButton | 带胶囊边框但颜色不起眼的按钮,使用 variant="subtle" 配合自定义边框颜色 |
| 下拉按钮 | ✅ | XButton split | 左主操作 + 右更多操作 |
| 分组按钮 | ✅ | XPillSelect / XFilterPills / XFormPills | 筛选、单选、多选 |
| Toggle 按切按钮 | ✅ | XToggleButton | 单选切换按钮组,支持 floating badge,适合 billing / tabs-like toggle / 视图切换 |
通用规则
- 两个组件都使用
cn(...)合并类名,内部基于twMerge(clsx(...))。 - 结论:使用方传入的
className如果和默认 Tailwind 类冲突,通常以后传入的为准。 - 适合外部覆盖的常见样式:
- 背景色:
bg-*from-*to-* - 边框:
border-*border-2 - 宽度:
w-*min-w-*max-w-* - hover:
hover:bg-*hover:border-*hover:text-*
- 背景色:
- 不要假设“所有样式都可改”。结构型样式、外层容器样式、
disabled/loading行为,很多是组件内部固定的。
组件选择
- 只需要单个按钮,优先用
XButton的type="single"。 - 需要渐变、链接跳转、图文按钮,用
GradientButton。 - 需要单选切换按钮组,用
XToggleButton。 - 需要主操作 + 下拉菜单,用
XButton的type="split"。 - 如果需求是深度定制 split 菜单项样式,当前
XButton split不够开放,可能要改源码。 - 要做图标按钮,优先用
XButton single,传图标,text: ''。 - 要做文字按钮,优先用
XButton single,传icon: null | false,配合variant="subtle"和自定义className来设置边框颜色。
GradientButton
文件:packages/third-ui/src/fuma/mdx/gradient-button.tsx
适用场景
- 可作为按钮,也可作为链接。
- 支持默认图标、loading、左右图标位置、对齐方式。
- 适合做强调型 CTA。
参数
title- 含义:按钮文案。
- 必填。
icon- 含义:自定义图标。
- 默认:不传时自动使用箭头图标;loading 时自动切为转圈图标。
iconForcePosition- 含义:强制图标在左或右。
- 可选值:
'left' | 'right' - 默认:有
onClick时偏左,无onClick时偏右。
align- 含义:按钮内容对齐,同时影响外层容器对齐。
- 可选值:
'left' | 'center' | 'right' - 默认:
'left'
disabled- 含义:禁用按钮。
- 默认:
false
className- 含义:按钮本体的自定义样式。
- 默认:空字符串。
iconClassName- 含义:图标样式。
- 默认:
h-4 w-4
href- 含义:链接地址。
- 默认:未传时为
"#"
openInNewTab- 含义:链接是否新开标签页。
- 默认:
true
preserveReferrer- 含义:新开标签页时是否保留 referrer。
- 默认:
false
onClick- 含义:点击事件。传了以后优先走按钮点击逻辑。
- 默认:不传
loadingText- 含义:loading 时显示的文案。
- 默认:
title的文本值,否则Loading...
preventDoubleClick- 含义:点击后是否进入 loading 并阻止重复点击。
- 默认:
true
variant- 含义:视觉风格。
- 可选值:
'default' | 'soft' | 'subtle' - 默认:
'default'
默认样式
- 基础尺寸:
h-11 px-8 - 基础排版:
text-base font-bold - 形状:
rounded-full - 有 focus ring
default:渐变背景 + 渐变 hover + 白字soft:主题背景 + 主题边框 + 轻微阴影subtle:浅背景 + 中性边框
可自定义项
- 可以通过
className改:- 背景色
- 渐变色
- 边框颜色和粗细
min-w-*/max-w-*/w-*- 高度和内边距
- hover 色
- 圆角
- 字号和字重
- 可以通过
iconClassName改图标大小和颜色。
强约束
- 外层还有一个包装
div,它的gap-3和对齐不是className控制的。 disabled/loading行为是固定的,不只是视觉变化。- loading 图标固定为
Loader2,默认图标固定为箭头。
示例
<GradientButton
title="Buy Now"
href="/pricing"
className="min-w-[160px] max-w-[240px] bg-black text-white hover:bg-neutral-800 border border-black"
/><GradientButton
title="Create Post"
onClick={handleCreate}
variant="soft"
className="bg-emerald-500 hover:bg-emerald-600 border-emerald-600 text-white rounded-xl px-6"
iconClassName="w-4 h-4 text-white"
/>XButton
文件:packages/third-ui/src/main/x-button.tsx
支持两种模式:single 和 split。
XButton Single
适用场景
- 纯点击按钮。
- 需要简单但稳定的样式扩展。
- 比
GradientButton更适合作为业务操作按钮。
参数
type- 固定:
'single'
- 固定:
button- 含义:主按钮配置。
- 结构:
icon: 图标,必填text: 文案,必填onClick: 点击事件,必填disabled?: 是否禁用,默认false
loadingText- 含义:loading 文案。
- 默认:
button.text,否则Loading...
minWidth- 含义:最小宽度 class。
- 默认:
min-w-[110px]
className- 含义:按钮本体自定义样式。
- 默认:空字符串。
iconClassName- 含义:图标样式。
- 默认:
w-5 h-5
variant- 含义:视觉风格。
- 可选值:
'default' | 'soft' | 'subtle' - 默认:
'default'
默认样式
- 移动端默认
w-full,桌面端sm:w-auto - 默认最小宽度:
min-w-[110px] - 基础排版:
text-sm font-semibold - 形状:
rounded-full
可自定义项
className可覆盖:- 背景色
- 边框
- hover 色
- 宽度 / 最大宽度
- 圆角
- 内边距
minWidth适合明确指定最小宽度。
强约束
- 点击后进入 loading,期间不可重复点击。
disabled/loading会叠加禁用态样式。- 默认是移动端占满宽度,如果不需要要主动覆盖。
示例
<XButton
type="single"
button={{ icon: <Plus />, text: "New", onClick: handleNew }}
minWidth="min-w-[140px]"
className="w-auto max-w-[220px] bg-blue-600 text-white hover:bg-blue-700 border border-blue-700"
/><XButton
type="single"
variant="subtle"
button={{ icon: <Download />, text: "Export", onClick: handleExport }}
className="border-neutral-400 hover:bg-neutral-100 rounded-md"
/>XButton Split
适用场景
- 左边主操作,右边展开更多操作。
- 适合轻度换肤,不适合深度改结构。
参数
type- 固定:
'split'
- 固定:
mainButton- 含义:左侧主按钮配置。
- 结构:
icon: 图标,必填text: 文案,必填onClick: 点击事件,必填disabled?: 是否禁用,默认false
menuItems- 含义:下拉菜单项数组。
- 每项结构:
icon: 图标,必填text: 文案,必填onClick: 点击事件,必填disabled?: 是否禁用,默认falsetag?: 右上角标记,结构为{ text, color? }splitTopBorder?: 是否显示顶部强调分割线
loadingText- 含义:loading 文案。
- 默认:
mainButton.text,否则Loading...
menuWidth- 含义:菜单宽度 class。
- 默认:
w-full sm:w-40
className- 含义:外层容器样式。
- 默认:空字符串。
mainButtonClassName- 含义:左侧主按钮样式。
- 默认:空字符串。
dropdownButtonClassName- 含义:右侧下拉按钮样式。
- 默认:空字符串。
iconClassName- 含义:按钮图标样式。
- 默认:
w-5 h-5
variant- 含义:视觉风格。
- 可选值:
'default' | 'soft' | 'subtle' - 默认:
'default'
默认样式
- 外层容器:
rounded-full - 左按钮:
flex-1 min-w-0 rounded-l-full - 右按钮:固定窄宽度,默认
w-9 sm:w-10 - 菜单面板:白底/暗色底,带边框和阴影
可自定义项
className:改整个 split 容器背景、边框、宽度mainButtonClassName:改单击主按钮背景、边框、hoverdropdownButtonClassName:改右侧下拉按钮背景、边框、hovermenuWidth:改下拉面板宽度
强约束
- 结构固定为“左主按钮 + 右下拉按钮”。
- 左右圆角分工固定;如果要改圆角,通常三处都要一起改。
- 右侧下拉按钮是窄按钮,天然不适合做普通按钮宽度。
- 菜单项本身没有暴露
className。 menuItems[].splitTopBorder使用内联样式画线,外部 class 不能直接覆盖这条线的颜色。- 菜单项 hover 样式当前基本写死,不能通过 props 精细控制。
示例
<XButton
type="split"
mainButton={{ icon: <Sparkles />, text: "Publish", onClick: handlePublish }}
menuItems={[
{ icon: <Clock3 />, text: "Schedule", onClick: handleSchedule },
{ icon: <Archive />, text: "Archive", onClick: handleArchive, splitTopBorder: true },
]}
className="bg-white border border-neutral-300"
mainButtonClassName="hover:bg-neutral-50"
dropdownButtonClassName="border-l border-neutral-300 hover:bg-neutral-50"
menuWidth="w-48"
/>XToggleButton
文件:packages/third-ui/src/main/x-toggle-button.tsx
适用场景
- billing 周期切换
- tabs-like 视图切换
- 单选 toggle 按钮组
参数
options- 含义:切换项数组
- 结构:
value: string,唯一标识(必填)label: ReactNode,项显示文案(必填)disabled?: boolean,单项禁用className?: string,单项额外样式badge?: ReactNode,floating badge 内容(可选,激活时显示)mobileIcon?: ReactNode,移动端显示的图标(可选,sm 以下显示)
- 必填
value- 含义:当前选中值
- 有值时为受控模式
defaultValue- 含义:默认选中值
- 无
value时生效
onChange- 含义:切换回调
- 默认:无
disabled- 含义:整体禁用
- 默认:
false
className- 含义:外层容器样式
itemClassName- 含义:所有按钮项的公共样式
activeItemClassName- 含义:激活项附加样式
inactiveItemClassName- 含义:未激活项附加样式
badgeClassName- 含义:
floating badge样式
- 含义:
minItemWidthClassName- 含义:每项最小宽度
- 默认:
min-w-[80px] sm:min-w-[100px] md:min-w-[120px]
maxItemWidthClassName- 含义:每项最大宽度(配合 truncate 实现文本截断)
- 默认:
max-w-[120px] sm:max-w-[160px]
itemTextClassName- 含义:按钮项内文字的大小和排版
- 默认:
text-xs sm:text-sm md:text-base
itemPaddingClassName- 含义:按钮项内左右上下边距
- 默认:
px-2 py-1.5 sm:px-3 sm:py-2
size- 含义:尺寸
- 可选值:
'default' | 'compact' - 默认:
'default'
fullWidth- 含义:是否整组铺满宽度,且每项等分
- 默认:
false
name- 含义:辅助标识
ariaLabel- 含义:无可见标题时建议传,提升可访问性
默认行为
- 支持受控和非受控两种模式
- 未传
value时,内部自动管理选中状态 - 未传
defaultValue时,默认选中第一项 - 点击当前已选项不会重复触发切换
- 单项可禁用,整组也可禁用
- mobileIcon 自动变白色:激活时,mobileIcon 会自动添加
text-whiteclass,配合 global-icon 的 className 覆盖机制,自动将主题色图标转为白色
默认样式
- 外层容器:白底/暗色底、浅边框、阴影、
rounded-full - 激活项:主题渐变背景 + 白字
- 未激活项:中性文字色
- 支持附着在按钮项上方、半内半外的
floating badge floating badge的样式参考当前 billing 切换实现,移动端可用
可自定义项
className- 改整体背景、边框、宽度、阴影
itemClassName- 改所有按钮项的公共圆角、内边距、字重
itemTextClassName- 改按钮项文字大小和排版,移动端响应式尺寸适配
itemPaddingClassName- 改按钮项内部左右上下边距,支持响应式调整
minItemWidthClassName- 改每个切换项最小宽度基线,支持响应式
maxItemWidthClassName- 改每个切换项最大宽度,防止文本无限扩张,配合 truncate 实现截断
activeItemClassName- 改选中项颜色、边框、阴影
inactiveItemClassName- 改未选中项颜色、hover
badgeClassName- 改 floating badge 的颜色、位置、尺寸
强约束
- 当前是单选 toggle,不是多选 toggle
- 当前只负责切换按钮组,不负责下方内容面板
- 内容展示应由业务层根据
value决定 - mobileIcon 白色化仅对 global-icon 有效;使用其他 icon 库时激活样式不变
移动端优化建议
- 使用 mobileIcon 时,建议减小 itemPaddingClassName 和 itemTextClassName(因为只显示图标)
- 对于有 mobileIcon 的 toggle,minItemWidthClassName 可以设置更小的移动端值(如
min-w-[40px] sm:min-w-[100px]) - mobileIcon 自动处理激活时变白,使用方无需重复传两个 icon
示例
<XToggleButton
value={billingType}
onChange={setBillingType}
options={[
{ value: 'monthly', label: 'Monthly' },
{ value: 'yearly', label: 'Yearly', badge: 'Save 20%' },
]}
/><XToggleButton
defaultValue="status"
size="compact"
fullWidth
options={[
{ value: 'status', label: 'Status' },
{ value: 'details', label: 'Details' },
{ value: 'stats', label: 'Stats', disabled: true },
]}
className="max-w-[360px]"
/>// 移动端响应式:移动端显示图标,web 端显示文本
<XToggleButton
value={viewMode}
onChange={setViewMode}
itemPaddingClassName="px-1 py-1 sm:px-3 sm:py-2"
minItemWidthClassName="min-w-[40px] sm:min-w-[100px]"
maxItemWidthClassName="max-w-[50px] sm:max-w-[160px]"
options={[
{ value: 'grid', label: 'Grid', mobileIcon: <icons.Grid className="h-4 w-4" /> },
{ value: 'list', label: 'List', mobileIcon: <icons.List className="h-4 w-4" /> },
{ value: 'detail', label: 'Detail', mobileIcon: <icons.Menu className="h-4 w-4" /> },
]}
/>AI 使用建议
- 要改背景、边框、宽度、hover,优先先传
className,不要急着改源码。 - 要改 split 模式下菜单项 hover、分隔线、每项 className,目前要改源码。
- 要求“最大最小宽度可控”时:
GradientButton:直接在className里写XButton single:优先用minWidth,再配合classNameXButton split:分别考虑容器、主按钮、下拉按钮,不要只改一处
- 要求“只是个业务按钮,不需要链接和渐变”时,优先
XButton single。
Pill / Tag 类按钮
文件:packages/third-ui/src/main/pill-select/index.ts
这一组虽然不是传统 CTA 按钮,但在业务使用上可以归到“按钮式选择 / 输入控件”:
XPillSelect- 通用胶囊选择器
- 支持单选、 多选、带输入扩展
XFilterPills- 过滤器型单选胶囊
XFormPills- 表单字段型单选胶囊
XTokenInput- 输入式 token/tag
- 可视为“输入式按钮”
组件选择
- 需要单选胶囊,用
XPillSelect mode="single"。 - 需要多选胶囊,用
XPillSelect mode="multiple"。 - 需要给用户边选边补充新值,用
XPillSelect + inputEnabled。 - 需要筛选器 UI,用
XFilterPills。 - 需要表单字段 UI,用
XFormPills。 - 需要自由输入多个 token/tag,用
XTokenInput。
XPillSelect
文件:packages/third-ui/src/main/pill-select/x-pill-select.tsx
适用场景
- 单选按钮组的胶囊版封装
- 多选按钮组的胶囊版封装
- 可选项 + 自由输入混合场景
参数
mode- 含义:选择模式。
- 可选值:
'single' | 'multiple' - 必填。
options- 含义:可选项数组。
- 结构:
{ label: string; value: string }[] - 默认:
[]
valuesingle模式:stringmultiple模式:string[]
onChangesingle模式:(value: string) => voidmultiple模式:(value: string[]) => void
disabled- 含义:是否禁用。
- 默认:
false
className- 含义:外层根节点样式。
- 默认:无
pillClassName- 含义:下拉面板中每个 option pill 的样式。
- 默认:无
emptyLabel- 含义:未选择时的占位文本。
- 默认:未传则可能显示空白占位区域
maxPillWidthClassName- 含义:已选 pill 和 option 文本的最大宽度 class。
- 默认:
max-w-[180px] sm:max-w-[220px]
size- 含义:尺寸。
- 可选值:
'default' | 'compact' - 默认:
'default'
inputEnabled- 含义:是否允许在下拉面板中输入新值。
- 默认:
false
inputPlaceholder- 含义:输入框占位符。
- 默认:无
onInputTransform- 含义:输入值提交前的转换函数。
- 默认:无
allSelectedLabel- 含义:多选全选时,用一个聚合 pill 替代所有 pill 的文案。
- 默认:未传时为
全部
maxVisiblePills- 含义:多选模式下,最多展示多少个已选 pill,超出显示
+N。 - 默认:不限制
- 含义:多选模式下,最多展示多少个已选 pill,超出显示
allowClear- 含义:再次点击已选项时是否可清空。
- 仅在
single/multiple模式可传。 - 默认:不传时视为
false
默认行为
- 点击主区域会展开 / 收起下拉面板。
- 点击外部会自动收起。
single模式选择后自动收起。multiple模式选择后不自动收起。inputEnabled时,输入框按Enter提交。- 输入值会自动去掉逗号并
trim()。 - 多选值会自动去重。
- 选中项会显示在主区域中,未选中时显示
emptyLabel。
默认样式
- 主区域:圆角整丸
rounded-full - 主区域边框:默认浅边框,hover/open 时切到主题边框色
default尺寸:min-h-11 px-4 py-2.5compact尺寸:min-h-9 px-3 py-1.5- 已选 pill:主题背景 + 主题文字色
- option pill:中性灰背景;选中后切换到主题背景/边框/文字
- 下拉面板:大圆角、浅边框、阴影
可自定义项
className- 适合改根容器宽度、布局、外边距
pillClassName- 适合改面板内 option pill 的背景、边框、hover、圆角
maxPillWidthClassName- 适合控制 pill 文本截断宽度
size- 适合切紧凑型布局
emptyLabel/allSelectedLabel- 适合控制未选和全选文案
inputEnabled/inputPlaceholder/onInputTransform- 适合做“半预设 + 半自由输入”场景
强约束
- 主触发区本身没有单独暴露
triggerClassName,不能直接只改外层按钮本体样式。 - 已选中的 pill 样式没有单独暴露
selectedPillClassName,只能跟随内部默认主题风格。 - 下拉面板容器样式没有单独 props,无法直接只改面板背景/边框/阴影。
single模式选中后自动关闭,这是固定行为。- 输入提交规则固定为
Enter提交,不支持传分隔符策略。
示例
<XPillSelect
mode="single"
value={status}
onChange={setStatus}
options={[
{ label: "Draft", value: "draft" },
{ label: "Published", value: "published" },
]}
emptyLabel="Select status"
pillClassName="rounded-md border border-neutral-300 hover:bg-neutral-100"
className="w-full max-w-[320px]"
/><XPillSelect
mode="multiple"
value={tags}
onChange={setTags}
options={tagOptions}
maxVisiblePills={3}
allSelectedLabel="All tags"
inputEnabled
inputPlaceholder="Add tag"
onInputTransform={(value) => value.toLowerCase()}
/>XFilterPills
文件:packages/third-ui/src/main/pill-select/x-filter-pills.tsx
适用场景
- 筛选器 UI
- 上方一个小标题,下方一个紧凑型单选胶囊
参数
label- 含义:筛选项标题。
value- 含义:当前值。
options- 含义:可选项。
onChange- 含义:值变化回调。
allLabel- 含义:自动插入到第一个位置的“全部”选项文案。
className- 含义:外层容器样式。
默认行为
- 内部固定使用
XPillSelect mode="single"。 - 自动将
{ label: allLabel, value: '' }插入 options 首位。 - 固定使用
size="compact"。 - 已选 pill 最大宽度更小:
max-w-[150px] sm:max-w-[220px]
可自定义项
- 只能通过
className改外层布局。 - 更深层的 pill 样式当前没有继续透传。
强约束
- 这是轻封装,适合快速使用,不适合深度换肤。
- 如果要自定义 pill 细节,直接改用
XPillSelect。
示例
<XFilterPills
label="Status"
value={status}
onChange={setStatus}
allLabel="All"
options={[
{ label: "Active", value: "active" },
{ label: "Paused", value: "paused" },
]}
className="w-[220px]"
/>XFormPills
文件:packages/third-ui/src/main/pill-select/x-form-pills.tsx
适用场景
- 表单字段型单选
- 带 label、空态文案、可选清空
参数
label- 含义:字段标题。
value- 含义:当前值。
options- 含义:可选项。
onChange- 含义:值变化回调。
emptyLabel- 含义:未选择时占位文案。
allowClear- 含义:再次点击已选项时是否清空。
- 默认:
false
className- 含义:外层容器样式。
默认行为
- 内部固定使用
XPillSelect mode="single"。 - 透传
allowClear。 - 已选 pill 最大宽度:
max-w-[150px] sm:max-w-[220px]
可自定义项
- 只能改外层容器。
- 如果要改具体 pill 风格,直接使用
XPillSelect。
示例
<XFormPills
label="Category"
value={category}
onChange={setCategory}
emptyLabel="Select category"
allowClear
options={[
{ label: "Article", value: "article" },
{ label: "News", value: "news" },
]}
/>XTokenInput
文件:packages/third-ui/src/main/pill-select/x-token-input.tsx
适用场景
- 输入多个标签、关键词、域名、邮箱片段等
- 可视化 token/tag 输入
- 可归类为“输入式按钮”
参数
value- 含义:当前 token 数组。
- 必填。
onChange- 含义:token 变化回调。
- 必填。
placeholder- 含义:没有 token 时输入框占位符。
- 默认:无
emptyLabel- 含义:没有 token 时输入框下方提示文案。
- 默认:无
disabled- 含义:是否禁用。
- 默认:
false
className- 含义:外层容器样式。
- 默认:无
maxPillWidthClassName- 含义:token pill 最大宽度 class。
- 默认:
max-w-[180px] sm:max-w-[220px]
size- 含义:尺寸。
- 可选值:
'default' | 'compact' - 默认:
'default'
默认行为
- 输入按
Enter提交 token。 - 失焦时自动提交当前 draft。
- 空输入按
Backspace时会删除最后一个 token。 - token 自动去掉逗号、去首尾空格、自动去重。
- 点击整个区域会聚焦输入框。
- 每个 token 自带删除按钮。
默认样式
- 容器:
rounded-3xl,不是rounded-full default尺寸:min-h-11 px-4 py-2.5compact尺寸:min-h-9 px-3 py-1.5- token pill:主题背景 + 主题文字色
- 删除按钮:小圆形 hover 背景 + focus ring
可自定义项
className- 适合改整体宽度、布局、外边距
maxPillWidthClassName- 控制 token 文本截断宽度
size- 切换紧凑模式
强约束
- token pill 样式没有单独
pillClassName,不能直接定制每个 token 的背景/边框。 - 输入框样式没有单独
inputClassName。 - 删除按钮样式没有单独 props。
- token 提交和删除交互是固定策略。
示例
<XTokenInput
value={keywords}
onChange={setKeywords}
placeholder="Type and press Enter"
emptyLabel="Add one or more keywords"
className="w-full max-w-[420px]"
maxPillWidthClassName="max-w-[120px]"
/><XTokenInput
value={domains}
onChange={setDomains}
size="compact"
placeholder="Add domain"
className="max-w-[320px]"
/>AI 使用建议
- 要快速做“胶囊单选/多选”,优先
XPillSelect。 - 要快速做“筛选器”或“表单字段”,优先
XFilterPills/XFormPills。 - 要自由输入多个值,优先
XTokenInput。 - 如果需求强调“每个已选 pill 的背景、边框、hover 都要高度可配置”,当前这组组件开放度一般,可能需要补 props 或改源码。
- 如果需求只是改宽度、布局、option pill 样式,优先先用现有的
className/pillClassName/maxPillWidthClassName。