生态产品多个设计器现已发布正式版

查看生态总览
跳转到内容

Form 表单 ​

管理一组字段的值、校验、提交和重置。

用法 ​

提交并校验表单

组件结构 ​

加粗的是必需部件。

data-scope="form":root · field-group · error-summary · error-summary-item · submit-trigger · reset-trigger

示例 ​

校验时机 ​

在失焦或输入时校验

状态 ​

禁用与只读表单

整表禁用

只读:能提交,改不动

异步校验 ​

提交前检查用户名

提交时先问一次服务端,占用的名字会被挡下

声明式规则 ​

配置字段校验规则

布局 ​

设置纵向、横向、行内或网格布局

排布

字段联动重验 ​

确认密码的规则声明 deps: ['password']:先填确认密码并离开,之后再改密码,确认密码会跟着重新校验

提交在途 ​

提交回调返回 Promise:落定之前 submitting 为真,提交钮报在途、再按也不会重复提交;拒绝经 submit-error 报出

尚未保存

设计指引 ​

何时使用 ​

  • 多个字段需要一起提交或校验。
  • 需要统一管理错误信息和校验时机。

何时不用 ​

  • 只有一个立即生效的控件时直接处理其值。
  • 只需要标签、说明和错误信息时使用表单字段。

特性 ​

  • validateOn 设置输入、失焦或提交时校验。
  • 支持声明式规则、自定义校验和异步校验。
  • 必填按空值判定:undefined、null、只含空白的字符串(含不换行空格与全角空格)、空数组与元素全为空的数组算空,false 与 0 不算空。「必须勾选同意」这类要求写 validator,判断值是否为 true。
  • min / max 对字符串按字素计数:组合 emoji、国旗、带变音符的字母各算一个字,与文本框的计数和 maxLength 同一口径,@xihan-ui/headless 导出的 graphemeLength 就是这把尺子;数组按元素个数,数值比较大小。数据库按列长限制时(如 PostgreSQL 的 varchar(n) 按码点计),后端仍要自己校验长度;.NET 端用 StringInfo.LengthInTextElements 得到同样的字数。
  • 正则以字符串保存时(模板、服务端下发的规则),用 compileFormPattern(source, { flags?, fullMatch?, maxLength? }) 编译成 pattern 所需的 RegExp:标志缺省只允许 imsu,源码缺省不超过 1000 个字符,失败时返回原因而不抛异常,fullMatch 包成整串匹配。它只检查语法,不判断回溯开销,来自不受信方的正则仍要在服务端限时执行。
  • 字段值、错误和校验状态均可受控。
  • 录入控件放进字段组即按字段名读写值表:控件自己没给值(value,开关与复选框是 checked,文件上传是 files)时,初值取 defaultValues(字段有值时控件自己的 defaultValue 等初值不生效),改动写回表单,表单重置或 setFieldValue 改了这个字段,控件跟着变;禁用、只读、必填与错误态照旧由表单下传。控件自己给了值就一概不接,作用域插槽里的 value / setValue 手动接法照旧可用。组里有几个控件就都接同一个字段(滑块配一个数字框显示同一个值);组件内部借来的子控件(颜色选择器里的滑块、分页里的每页条数下拉)与字段边界后面的实例不接。
  • 值表变化来自某个字段组的行变更时,onValuesChange(事件名 values-change)的载荷另带 arrayMutation: { name, mutation }:是哪一个字段组、哪一种行变更,与字段组的 value-change 同一份。
  • 错误汇总可跳转到对应字段。
  • 支持纵向、横向、行内和网格布局。
  • 嵌套字段与字段数组使用显式 FormPath。
  • 值类型:根组件按 defaultValues(或 values)推断整表的值类型,rules 与 validate 交回的键只能写它的字段,onSubmit 拿到的 values 与 setFieldValue(name, value) 的值都按字段收窄,字段名拼错编译即报错。字段组推断不到根上的值类型:const { FieldGroup, ErrorSummaryItem } = defineFormFields<SignUp>()(Vue、React)拿到的就是字段组与错误摘要条目本身,只在类型上收窄,name 只能写字段(数组路径逐段检查),插槽里的 value 是那个字段的类型。Web Components 写 querySelector<XhFormElement<SignUp>>('xh-form')。
  • schema 收 Standard Schema:zod、valibot、arktype 的 schema 直接交进去,组件只依赖接口、不引入任何校验库。交给它的是嵌套对象(数组路径的字段按段写回原位),它报的问题按 path 落到字段上,同一字段只留第一条,没有路径的记在空字段名 '' 下;同一字段几边都报错时 rules 优先、validate 其次、schema 最后。schema 转换后的输出不用,onSubmit 交出的仍是表单的值。
  • 字段级状态:api.dirty 与 isFieldDirty(name) 相对 defaultValues 按结构比(改回原样即不算改过),isFieldTouched(name) 记失焦过一次的字段;resetField(name) 只把一个字段的值、错误与触碰标记还原。
  • 不提交的校验:validateAll() 整表校验并整表替换错误表,validateField(name) / validateFields(names) 只写回涉及的字段;三者都返回 Promise<{ valid, errors, stale }>,不触发 onSubmit / onInvalid、不显示错误摘要,也不搬焦点。校验结束前值被改则 stale 为真、结果不写回;校验器抛错时 Promise 拒绝,同时照常报 onValidationError。
  • 字段联动:规则写 deps: ['password'],依赖字段一改,本字段在被触碰过(校验时机不是 submit)或正挂着错误时重新校验,validator 的第二个参数读得到依赖字段的新值。
  • 提交在途:onSubmit 返回 thenable 时 submitting 为真直到它落定,期间再提交不发生,提交钮带 aria-disabled 与 data-loading、表单报 aria-busy;拒绝经 onSubmitError(三端事件 submit-error)报出原始原因。Web Components 的事件拿不到监听函数的返回值,异步提交交给 submitAction property。

组合 ​

最佳实践 ​

  • 首次校验优先放在失焦或提交时。
  • 提交失败后聚焦第一个错误字段。
  • 异步校验期间显示明确的加载状态。
  • 异步提交让 onSubmit 直接返回 Promise,用 submitting 改提交钮文字,不另外维护一个加载标志。

反模式 ​

  • 在用户尚未尝试提交时持续显示全部错误。
  • 只在前端执行关键业务校验。

API 参考 ​

产物 ​

层值
自定义元素<xh-form>
Vue 组件XhFormErrorSummary XhFormErrorSummaryItem XhFormFieldGroup XhFormResetTrigger XhFormRoot XhFormSubmitTrigger
组合式函数useForm
状态机formMachine
皮肤@xihan-ui/styles/form.css

Props ​

属性类型必填说明
valuesTValues受控值表;提供即受控:cell 直读 prop,写入只发 onValuesChange 不落内部值。
defaultValuesTValues非受控初值,同时也是 reset 的落点。
errorsFormErrorPatch受控错误表;提供即受控。空串会被清理(空串不是一条错误)。
defaultErrorsFormErrorPatch
validate(values: TValues) => FormErrorPatchOf<TValues> | Promise<FormErrorPatchOf<TValues>>校验函数。返回字段名 → 错误文案,无错的字段给空串或省略; 允许返回 Promise(远程校验),期间 validating 置真。 与 rules 并用时同字段两边都报错按 rules 的文案计算。
schemaFormStandardSchemaStandard Schema(zod、valibot、arktype 的 schema 都是):校验时把整表的值交给它, 它报的问题按路径落到字段上——一段的字符串路径即字段名,多段的即数组路径(数组行、嵌套对象); 没有路径的问题记在空字段名 '' 下,提交照样被拦下。与 validate、rules 并用:同字段几边都报错时 rules 的文案优先、其次 validate,再次 schema。只用它的问题,不用它的输出值:提交交出去的仍是表单的值。
rulesFormRules<TValues>声明式校验规则:字段名 → 一条或一组规则,与 validate 可并用。
validateMessagesFormValidateMessages规则文案模板,{name}/{min}/{max} 现场代入;逐条压在 translations 之上,都没给的取英文语言包。
translationsPartial<FormTranslations>校验报错的文案模板,形状同 validateMessages;全局语言包经它到达。
validateOnFormValidateOn校验时机,默认 submit。
layoutFormLayout排布,默认 vertical。
columnsFormColumnsgrid 排布下的列数:1 至 4 的整数,未提供时按一列排列;范围外的值也按一列排列。 也接受断点对象 { base, sm, md, lg, xl },逐档写各自的列数,未写的档沿用更窄的一档。 其余三档排布下不参与排版。
labelWidthnumber | stringhorizontal 下标签列宽(number 视作 px),整表统一、字段据此对齐。
labelAlign'start' | 'end'horizontal 下标签文字的对齐缘,默认 end(贴近控件)。
disabledboolean整个表单禁用:提交、重置、写值一概不发生,两个按钮带原生 disabled。
readOnlyboolean只读:写值与重置不发生,但仍可提交。
onValuesChange(details: FormValuesChangeDetails<TValues>) => void值表变化意图回调;受控时是唯一出口,非受控时随内部写入一并通知。
onErrorsChange(details: FormErrorsChangeDetails) => void错误表变化意图回调;受控时是唯一出口。
onSubmit(details: FormSubmitDetails<TValues>) => void | PromiseLike<unknown>校验通过才调用。返回 thenable 时 submitting 为真直到它落定:期间再提交不发生、提交钮报在途; 拒绝经 onSubmitError 报出。同步抛出照常向上抛,不当作提交失败。
onSubmitError(details: FormSubmitErrorDetails<TValues>) => void提交回调返回的 thenable 拒绝时调用。
onInvalid(details: FormInvalidDetails<TValues>) => void校验不通过时调用,附带拦截的整张错误表。
onValidationError(details: FormValidationErrorDetails<TValues>) => void校验器抛错或拒绝 Promise 时调用;不触发 onInvalid 或 onSubmit。

事件 ​

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

事件载荷说明
values-changeFormValuesChangeDetails值表变化;detail 为 { values, arrayMutation? },来自字段组的行变更时 arrayMutation 是 { name, mutation }
errors-changeFormErrorsChangeDetails错误表变化;detail 为 { errors }
submitFormSubmitDetails校验通过才派发;detail 为 { values }。异步提交门经 submitAction 属性:事件拿不到监听函数的返回值, 给元素赋 submitAction = details =&gt; thenable 即在提交通过校验后调用它,期间 submitting 为真、提交钮报在途
submit-errorFormSubmitErrorDetailssubmitAction 返回的 thenable 拒绝;detail 为 { cause, values },保留原始原因
invalidFormSubmitDetails校验不通过时派发;detail 为 { errors, values }
validation-errorFormValidationErrorDetails校验器执行异常;detail 为 { cause, values, field },field 为 null 表示整表提交

插槽 ​

仅列出带载荷的插槽。

Vue 组件插槽载荷说明
XhFormErrorSummarydefaultFormErrorSummarySlotProps
XhFormErrorSummaryItemdefaultFormErrorSummaryItemSlotProps
XhFormFieldGroupdefaultFormFieldGroupSlotProps
XhFormRootdefaultFormRootSlotProps

React 适配器 props ​

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

React 组件属性类型必填说明
XhFormErrorSummarychildrenSlotChildren<FormErrorSummarySlotProps>
XhFormErrorSummaryItemnameP是该条指向哪个字段路径。
XhFormErrorSummaryItemchildrenSlotChildren<FormErrorSummaryItemSlotProps<P>>
XhFormFieldGroupnameP是字段路径;字符串含点仍是单键,数组才表示层级。
XhFormFieldGroupspanFormFieldSpangrid 排布下该格占多宽:1 至 4 跨相应列数,'full' 占满整行;未写时占一列。
XhFormFieldGroupchildrenSlotChildren<FormFieldGroupSlotProps<TValues, P>>
XhFormRootchildrenSlotChildren<FormRootSlotProps<TValues>>

状态 ​

公开状态写入 data-state。

部件取值
root'invalid' | 'idle'
error-summary'invalid' | 'idle'

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

状态:idle · invalid

事件:SUBMIT · RESET · FIELD.RESET · VALIDATE · SUBMIT.SETTLED · VALIDATION.PASS · VALIDATION.FAIL · FIELD.SET · FIELD.ARRAY.MUTATE · FIELD.BLUR · ERROR.SET · ERRORS.CLEAR · ERROR.FOCUS · PRESS.START · PRESS.END · SUMMARY.SNAPSHOT · SUMMARY.RENDERED

判据:isEnabled · isEditable · isValidationSnapshotCurrent · canPress · isSubmitting

connect API ​

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

成员类型说明
valuesTValues当前的值表。
errorsFormErrors当前的错误表(已清理)。
errorNamesFormPath[]出错的字段名,插入顺序。
errorCountnumber
invalidboolean错误表非空。与是否提交失败过无关,挂载时作者预置的错误也计入。
submitFailedboolean上一次提交被拦截:错误摘要据此显示。
validatingboolean异步校验进行中(提交或逐字段都计入)。
submittingboolean提交回调返回的 thenable 尚未落定:期间再提交不发生,提交钮报在途。
dirtyboolean有字段的值与 defaultValues 不同(按结构比,受控时同样以 defaultValues 为基准)。
validationErrorFormValidationErrorDetails<TValues> | null校验服务异常;null 表示没有异常,字段错误仍从 errors 读取。
disabledboolean
readOnlyboolean
validateOnFormValidateOn
layoutFormLayout当前的排布档。
getFieldId(name: FormFieldPath<TValues>) => string字段容器的 DOM id;错误摘要的链接指向它。
getFieldValue<P extends FormFieldPath<TValues>>(name: P) => FormPathValue<TValues, P> | undefined
getFieldError(name: FormFieldPath<TValues>) => string | undefined该字段当前的错误文案;无错时为 undefined。
summaryErrorsFormErrors错误摘要此刻画的那版错误表:平时就是 errors;错误全改完、摘要播退场的那几帧里是撤下之前的最后一版, 摘要里的文案与条数照它写,才不会先于摘要一起消失。
summaryErrorNamesFormPath[]summaryErrors 里出错的字段名,插入顺序。
summaryErrorCountnumber
getSummaryError(name: FormFieldPath<TValues>) => string | undefined错误摘要此刻给该字段画的文案;摘要里的条目读它而不读 getFieldError。
isFieldInvalid(name: FormFieldPath<TValues>) => boolean
isFieldDirty(name: FormFieldPath<TValues>) => boolean该字段的值与 defaultValues 里的不同(按结构比)。
isFieldTouched(name: FormFieldPath<TValues>) => boolean该字段失焦过一次。
isFieldRequired(name: FormFieldPath<TValues>) => boolean该字段的规则中声明了 required:字段的必填标记由此推导。
setFieldValue<P extends FormFieldPath<TValues>>(name: P, value: FormPathValue<TValues, P>) => void写一个字段的值;禁用或只读时不生效。
setFieldError(name: FormFieldPath<TValues>, message?: string) => void写一个字段的错误;未提供文案(或提供空串)即清除该条。
clearErrors() => void
submit() => void执行完整的校验与提交流程,与用户按提交键走同一路径。
validateAll() => Promise<FormValidateResult>整表校验一次、整表替换错误表,但不提交:不触发 onSubmit / onInvalid,不显示错误摘要,也不搬焦点。
validateField(name: FormFieldPath<TValues>) => Promise<FormValidateResult>只校验一个字段并只写回它的那一条。
validateFields(names: readonly FormFieldPath<TValues>[]) => Promise<FormValidateResult>逐字段校验这几个字段并只写回它们。
resetField(name: FormFieldPath<TValues>) => void只重置一个字段:值、错误与触碰标记回到初始;禁用或只读时不生效。
reset() => void值与错误都回到初始;禁用或只读时不生效。
getRootProps() => T['element']
getFieldGroupProps(props: FormFieldGroupProps<TValues>) => T['element']
getErrorSummaryProps() => T['element']
getErrorSummaryItemProps(props: FormErrorSummaryItemProps<TValues>) => T['element']
getSubmitTriggerProps() => T['button']
getResetTriggerProps() => T['button']

无障碍 ​

键盘 ​

规格出处:W3C APG

按键生效条件行为
Enter / Spaceheld on submit-trigger / reset-trigger / error-summary-item, not disabled, no async validation in flight按住期间该部件投影 data-pressed,与指针 :active 同一副按压面;抬起或失焦撤下,异步校验开跑(提交在途)或条目所指字段改好时一并撤下

ARIA ​

以下属性由 connect 生成。

部件属性值
rootaria-busy'true' | undefined
error-summaryaria-atomic'true'
error-summaryaria-live'assertive'
error-summaryrole'alert'
submit-triggeraria-busy'true' | undefined
submit-triggeraria-disabled'true' | undefined

样式参考 ​

皮肤 ​

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

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

数据属性 ​

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

部件属性值
rootdata-columnscolumns.base
rootdata-columns-lgcolumns.lg
rootdata-columns-mdcolumns.md
rootdata-columns-smcolumns.sm
rootdata-columns-xlcolumns.xl
rootdata-disabled''(条件成立时才出现)
rootdata-invalid''(条件成立时才出现)
rootdata-label-alignprops.labelAlign
rootdata-layoutprops.layout
rootdata-readonly''(条件成立时才出现)
rootdata-state'invalid' | 'idle'
field-groupdata-disabled''(条件成立时才出现)
field-groupdata-invalid''(条件成立时才出现)
field-groupdata-readonly''(条件成立时才出现)
field-groupdata-spanfieldSpan(field.span)
error-summarydata-countString(summaryErrorNames.length)
error-summarydata-state'invalid' | 'idle'
error-summary-itemdata-invalid''(条件成立时才出现)
error-summary-itemdata-pressed''(条件成立时才出现)
submit-triggerdata-disabled''(条件成立时才出现)
submit-triggerdata-loading''(条件成立时才出现)
submit-triggerdata-pressed''(条件成立时才出现)
submit-triggerdata-xh-action-control''
submit-triggerdata-xh-action-display'always'
submit-triggerdata-xh-action-profile'text'
submit-triggerdata-xh-action-size'md'
submit-triggerdata-xh-action-variant'solid'
submit-triggerdata-xh-ink-surface''
submit-triggerdata-xh-loading-ring'overlay'
reset-triggerdata-disabled''(条件成立时才出现)
reset-triggerdata-pressed''(条件成立时才出现)
reset-triggerdata-xh-action-control''
reset-triggerdata-xh-action-display'always'
reset-triggerdata-xh-action-profile'text'
reset-triggerdata-xh-action-size'md'
reset-triggerdata-xh-action-variant'outline'

CSS 变量 ​

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

变量部件CSS 属性状态默认来源说明
--xh-form-field-gapfield-groupgapdefault--xh-space-1form 的 field-group 部件 gap 覆盖槽。
--xh-form-gaprootcolumn-gap
margin-block-end
layout=grid
where(:not(:last-child)
where([data-scope='form'][data-part='root'])
--xh-space-5form 的 root 部件 column-gap、margin-block-end 覆盖槽。
--xh-form-inline-gaprootcolumn-gaplayout=inline--xh-space-6form 的 root 部件 column-gap 覆盖槽。
--xh-form-inline-row-gaprootmargin-block-endlayout=inline
where(:not(:last-child)
where([data-scope='form'][data-part='root'])
--xh-space-2form 的 root 部件 margin-block-end 覆盖槽。
--xh-form-label-wrootgrid-template-columnslayout=horizontalcalc(100% * 5 / 24)form 的 root 部件 grid-template-columns 覆盖槽。
--xh-form-loading-durationsubmit-triggeranimationxh-loading-ring--xh-motion-loop-spinform 的 submit-trigger 部件 animation 覆盖槽。
--xh-form-submit-bgsubmit-trigger--xh-ink-surface
background-color
default
xh-ink-surface
--xh-_action-variant-bg-restform 的 submit-trigger 部件 --xh-ink-surface、background-color 覆盖槽。
--xh-form-submit-bg-activesubmit-triggerbackground-colordisabled
is(:active, [data-pressed])
loading
not([data-disabled])
not([data-loading])
pressed
--xh-_action-variant-bg-pressedform 的 submit-trigger 部件 background-color 覆盖槽。
--xh-form-submit-bg-hoversubmit-triggerbackground-colordisabled
hover
loading
not([data-disabled])
not([data-loading])
--xh-_action-variant-bg-hoverform 的 submit-trigger 部件 background-color 覆盖槽。
--xh-form-submit-bordersubmit-triggerborderdefault--xh-_action-variant-border-restform 的 submit-trigger 部件 border 覆盖槽。
--xh-form-submit-border-activesubmit-triggerborder-colordisabled
is(:active, [data-pressed])
loading
not([data-disabled])
not([data-loading])
pressed
--xh-_action-variant-border-pressedform 的 submit-trigger 部件 border-color 覆盖槽。
--xh-form-submit-border-hoversubmit-triggerborder-colordisabled
hover
loading
not([data-disabled])
not([data-loading])
--xh-_action-variant-border-hoverform 的 submit-trigger 部件 border-color 覆盖槽。
--xh-form-submit-fgsubmit-triggerborder
border-color
color
@media (prefers-reduced-motion: reduce)
@media print
default
disabled
hover
is(:active, [data-pressed])
loading
motion=reduce
not([data-disabled])
not([data-loading])
pressed
where([data-motion='reduce'])
xh-loading-ring
xh-loading-ring=overlay
--xh-_action-variant-fg-hover
--xh-_action-variant-fg-loading
--xh-_action-variant-fg-pressed
--xh-_action-variant-fg-rest
form 的 submit-trigger 部件 border、border-color、color 覆盖槽。
--xh-form-submit-shadowsubmit-triggerbox-shadowdefault
disabled
hover
is(:active, [data-pressed])
loading
not([data-disabled])
not([data-loading])
pressed
--xh-_highlight-brandform 的 submit-trigger 部件 box-shadow 覆盖槽。
--xh-form-summary-bgerror-summarybackgrounddefault--xh-bg-surfaceform 的 error-summary 部件 background 覆盖槽。
--xh-form-summary-bordererror-summaryborderdefault--xh-border-invalidform 的 error-summary 部件 border 覆盖槽。
--xh-form-summary-fgerror-summarycolordefault--xh-fg-dangerform 的 error-summary 部件 color 覆盖槽。
--xh-form-summary-font-sizeerror-summaryfont-sizedefault--xh-text-body-sizeform 的 error-summary 部件 font-size 覆盖槽。
--xh-form-summary-gaperror-summarygapdefault--xh-space-1_5form 的 error-summary 部件 gap 覆盖槽。
--xh-form-summary-item-bgerror-summary-itembackgrounddefaulttransparentform 的 error-summary-item 部件 background 覆盖槽。
--xh-form-summary-item-bg-hovererror-summary-itembackgroundhover--xh-bg-subtleform 的 error-summary-item 部件 background 覆盖槽。
--xh-form-summary-item-bg-pressederror-summary-itembackgroundis(:active, [data-pressed])
pressed
--xh-bg-subtle-hoverform 的 error-summary-item 部件 background 覆盖槽。
--xh-form-summary-item-fg-hovererror-summary-itemcolorhover
is(:active, [data-pressed])
pressed
--xh-fg-danger-hoverform 的 error-summary-item 部件 color 覆盖槽。
--xh-form-summary-item-font-sizeerror-summary-itemfont-sizedefault--xh-text-secondary-sizeform 的 error-summary-item 部件 font-size 覆盖槽。
--xh-form-summary-item-pxerror-summary-itempadding-inlinedefault--xh-space-1form 的 error-summary-item 部件 padding-inline 覆盖槽。
--xh-form-summary-item-radiuserror-summary-itemborder-radiusdefault--xh-shape-controlform 的 error-summary-item 部件 border-radius 覆盖槽。
--xh-form-summary-item-underline-offseterror-summary-itemtext-underline-offsetdefault--xh-space-0_5form 的 error-summary-item 部件 text-underline-offset 覆盖槽。
--xh-form-summary-pxerror-summarypadding-inlinedefault--xh-control-px-mdform 的 error-summary 部件 padding-inline 覆盖槽。
--xh-form-summary-pyerror-summarypadding-blockdefault--xh-space-3form 的 error-summary 部件 padding-block 覆盖槽。
--xh-form-summary-radiuserror-summaryborder-radiusdefault--xh-shape-surfaceform 的 error-summary 部件 border-radius 覆盖槽。
--xh-form-summary-shadowerror-summarybox-shadowdefaultnoneform 的 error-summary 部件 box-shadow 覆盖槽。
--xh-form-trigger-bgreset-trigger--xh-ink-surface
background-color
default
xh-ink-surface
--xh-_action-variant-bg-restform 的 reset-trigger 部件 --xh-ink-surface、background-color 覆盖槽。
--xh-form-trigger-bg-activereset-triggerbackground-colordisabled
is(:active, [data-pressed])
loading
not([data-disabled])
not([data-loading])
pressed
--xh-_action-variant-bg-pressedform 的 reset-trigger 部件 background-color 覆盖槽。
--xh-form-trigger-bg-disabledreset-trigger
submit-trigger
--xh-ink-surface
background-color
disabled
xh-ink-surface
--xh-_action-variant-bg-disabledform 的 reset-trigger、submit-trigger 部件 --xh-ink-surface、background-color 覆盖槽。
--xh-form-trigger-bg-hoverreset-triggerbackground-colordisabled
hover
loading
not([data-disabled])
not([data-loading])
--xh-_action-variant-bg-hoverform 的 reset-trigger 部件 background-color 覆盖槽。
--xh-form-trigger-borderreset-triggerborderdefault--xh-_action-variant-border-restform 的 reset-trigger 部件 border 覆盖槽。
--xh-form-trigger-border-disabledreset-trigger
submit-trigger
border-colordisabled--xh-_action-variant-border-disabledform 的 reset-trigger、submit-trigger 部件 border-color 覆盖槽。
--xh-form-trigger-border-hoverreset-triggerborder-colordisabled
hover
loading
not([data-disabled])
not([data-loading])
--xh-_action-variant-border-hoverform 的 reset-trigger 部件 border-color 覆盖槽。
--xh-form-trigger-fgreset-triggercolordefault
disabled
hover
is(:active, [data-pressed])
loading
not([data-disabled])
not([data-loading])
pressed
--xh-_action-variant-fg-hover
--xh-_action-variant-fg-pressed
--xh-_action-variant-fg-rest
form 的 reset-trigger 部件 color 覆盖槽。
--xh-form-trigger-font-sizereset-trigger
submit-trigger
font-sizedefault--xh-text-body-sizeform 的 reset-trigger、submit-trigger 部件 font-size 覆盖槽。
--xh-form-trigger-hreset-trigger
submit-trigger
block-sizedefault--xh-_action-profile-visual-sizeform 的 reset-trigger、submit-trigger 部件 block-size 覆盖槽。
--xh-form-trigger-pxreset-trigger
submit-trigger
padding-inlinedefault--xh-_action-profile-padding-inlineform 的 reset-trigger、submit-trigger 部件 padding-inline 覆盖槽。
--xh-form-trigger-radiusreset-trigger
submit-trigger
border-radiusdefault--xh-shape-controlform 的 reset-trigger、submit-trigger 部件 border-radius 覆盖槽。

动效 ​

动效角色:按压 · 状态 · 出现 · 循环(见动效规范)。

可覆盖的动效槽:--xh-form-loading-duration。

共享关键帧 xh-drop-in · xh-fade-out 由 family/motion.css 提供,皮肤 @import 它,单独引入仍成立;background-color · color 走 transition 过渡。时长与缓动读动效令牌,改令牌即改全局节奏。

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

响应式 ​

皮肤按视口分档:min-width: 1024px · min-width: 1280px · min-width: 640px · min-width: 768px。

RTL ​

皮肤用逻辑属性排布(inline-start 一族),dir="rtl" 下自动镜像。

Released under The MIT License