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 的事件拿不到监听函数的返回值,异步提交交给submitActionproperty。
组合
最佳实践
- 首次校验优先放在失焦或提交时。
- 提交失败后聚焦第一个错误字段。
- 异步校验期间显示明确的加载状态。
- 异步提交让
onSubmit直接返回 Promise,用submitting改提交钮文字,不另外维护一个加载标志。
反模式
- 在用户尚未尝试提交时持续显示全部错误。
- 只在前端执行关键业务校验。
API 参考
产物
| 层 | 值 |
|---|---|
| 自定义元素 | <xh-form> |
| Vue 组件 | XhFormErrorSummary XhFormErrorSummaryItem XhFormFieldGroup XhFormResetTrigger XhFormRoot XhFormSubmitTrigger |
| 组合式函数 | useForm |
| 状态机 | formMachine |
| 皮肤 | @xihan-ui/styles/form.css |
Props
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
values | TValues | 受控值表;提供即受控:cell 直读 prop,写入只发 onValuesChange 不落内部值。 | |
defaultValues | TValues | 非受控初值,同时也是 reset 的落点。 | |
errors | FormErrorPatch | 受控错误表;提供即受控。空串会被清理(空串不是一条错误)。 | |
defaultErrors | FormErrorPatch | ||
validate | (values: TValues) => FormErrorPatchOf<TValues> | Promise<FormErrorPatchOf<TValues>> | 校验函数。返回字段名 → 错误文案,无错的字段给空串或省略; 允许返回 Promise(远程校验),期间 validating 置真。 与 rules 并用时同字段两边都报错按 rules 的文案计算。 | |
schema | FormStandardSchema | Standard Schema(zod、valibot、arktype 的 schema 都是):校验时把整表的值交给它, 它报的问题按路径落到字段上——一段的字符串路径即字段名,多段的即数组路径(数组行、嵌套对象); 没有路径的问题记在空字段名 '' 下,提交照样被拦下。与 validate、rules 并用:同字段几边都报错时 rules 的文案优先、其次 validate,再次 schema。只用它的问题,不用它的输出值:提交交出去的仍是表单的值。 | |
rules | FormRules<TValues> | 声明式校验规则:字段名 → 一条或一组规则,与 validate 可并用。 | |
validateMessages | FormValidateMessages | 规则文案模板,{name}/{min}/{max} 现场代入;逐条压在 translations 之上,都没给的取英文语言包。 | |
translations | Partial<FormTranslations> | 校验报错的文案模板,形状同 validateMessages;全局语言包经它到达。 | |
validateOn | FormValidateOn | 校验时机,默认 submit。 | |
layout | FormLayout | 排布,默认 vertical。 | |
columns | FormColumns | grid 排布下的列数:1 至 4 的整数,未提供时按一列排列;范围外的值也按一列排列。 也接受断点对象 { base, sm, md, lg, xl },逐档写各自的列数,未写的档沿用更窄的一档。 其余三档排布下不参与排版。 | |
labelWidth | number | string | horizontal 下标签列宽(number 视作 px),整表统一、字段据此对齐。 | |
labelAlign | 'start' | 'end' | horizontal 下标签文字的对齐缘,默认 end(贴近控件)。 | |
disabled | boolean | 整个表单禁用:提交、重置、写值一概不发生,两个按钮带原生 disabled。 | |
readOnly | boolean | 只读:写值与重置不发生,但仍可提交。 | |
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-change | FormValuesChangeDetails | 值表变化;detail 为 { values, arrayMutation? },来自字段组的行变更时 arrayMutation 是 { name, mutation } |
errors-change | FormErrorsChangeDetails | 错误表变化;detail 为 { errors } |
submit | FormSubmitDetails | 校验通过才派发;detail 为 { values }。异步提交门经 submitAction 属性:事件拿不到监听函数的返回值, 给元素赋 submitAction = details => thenable 即在提交通过校验后调用它,期间 submitting 为真、提交钮报在途 |
submit-error | FormSubmitErrorDetails | submitAction 返回的 thenable 拒绝;detail 为 { cause, values },保留原始原因 |
invalid | FormSubmitDetails | 校验不通过时派发;detail 为 { errors, values } |
validation-error | FormValidationErrorDetails | 校验器执行异常;detail 为 { cause, values, field },field 为 null 表示整表提交 |
插槽
仅列出带载荷的插槽。
| Vue 组件 | 插槽 | 载荷 | 说明 |
|---|---|---|---|
XhFormErrorSummary | default | FormErrorSummarySlotProps | |
XhFormErrorSummaryItem | default | FormErrorSummaryItemSlotProps | |
XhFormFieldGroup | default | FormFieldGroupSlotProps | |
XhFormRoot | default | FormRootSlotProps |
React 适配器 props
只列各组件自己声明的那些:继承自 ComponentPropsWithRef 的 DOM 属性不在其中,根组件上与上面 Props 表同名的也不重复列。Vue 的对应物是上面的插槽表。
| React 组件 | 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
XhFormErrorSummary | children | SlotChildren<FormErrorSummarySlotProps> | ||
XhFormErrorSummaryItem | name | P | 是 | 该条指向哪个字段路径。 |
XhFormErrorSummaryItem | children | SlotChildren<FormErrorSummaryItemSlotProps<P>> | ||
XhFormFieldGroup | name | P | 是 | 字段路径;字符串含点仍是单键,数组才表示层级。 |
XhFormFieldGroup | span | FormFieldSpan | grid 排布下该格占多宽:1 至 4 跨相应列数,'full' 占满整行;未写时占一列。 | |
XhFormFieldGroup | children | SlotChildren<FormFieldGroupSlotProps<TValues, P>> | ||
XhFormRoot | children | SlotChildren<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() 返回对应部件的宿主属性。
| 成员 | 类型 | 说明 |
|---|---|---|
values | TValues | 当前的值表。 |
errors | FormErrors | 当前的错误表(已清理)。 |
errorNames | FormPath[] | 出错的字段名,插入顺序。 |
errorCount | number | |
invalid | boolean | 错误表非空。与是否提交失败过无关,挂载时作者预置的错误也计入。 |
submitFailed | boolean | 上一次提交被拦截:错误摘要据此显示。 |
validating | boolean | 异步校验进行中(提交或逐字段都计入)。 |
submitting | boolean | 提交回调返回的 thenable 尚未落定:期间再提交不发生,提交钮报在途。 |
dirty | boolean | 有字段的值与 defaultValues 不同(按结构比,受控时同样以 defaultValues 为基准)。 |
validationError | FormValidationErrorDetails<TValues> | null | 校验服务异常;null 表示没有异常,字段错误仍从 errors 读取。 |
disabled | boolean | |
readOnly | boolean | |
validateOn | FormValidateOn | |
layout | FormLayout | 当前的排布档。 |
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。 |
summaryErrors | FormErrors | 错误摘要此刻画的那版错误表:平时就是 errors;错误全改完、摘要播退场的那几帧里是撤下之前的最后一版, 摘要里的文案与条数照它写,才不会先于摘要一起消失。 |
summaryErrorNames | FormPath[] | summaryErrors 里出错的字段名,插入顺序。 |
summaryErrorCount | number | |
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 / Space | held on submit-trigger / reset-trigger / error-summary-item, not disabled, no async validation in flight | 按住期间该部件投影 data-pressed,与指针 :active 同一副按压面;抬起或失焦撤下,异步校验开跑(提交在途)或条目所指字段改好时一并撤下 |
ARIA
以下属性由 connect 生成。
| 部件 | 属性 | 值 |
|---|---|---|
root | aria-busy | 'true' | undefined |
error-summary | aria-atomic | 'true' |
error-summary | aria-live | 'assertive' |
error-summary | role | 'alert' |
submit-trigger | aria-busy | 'true' | undefined |
submit-trigger | aria-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 生成;条件不成立时不输出无值属性。
| 部件 | 属性 | 值 |
|---|---|---|
root | data-columns | columns.base |
root | data-columns-lg | columns.lg |
root | data-columns-md | columns.md |
root | data-columns-sm | columns.sm |
root | data-columns-xl | columns.xl |
root | data-disabled | ''(条件成立时才出现) |
root | data-invalid | ''(条件成立时才出现) |
root | data-label-align | props.labelAlign |
root | data-layout | props.layout |
root | data-readonly | ''(条件成立时才出现) |
root | data-state | 'invalid' | 'idle' |
field-group | data-disabled | ''(条件成立时才出现) |
field-group | data-invalid | ''(条件成立时才出现) |
field-group | data-readonly | ''(条件成立时才出现) |
field-group | data-span | fieldSpan(field.span) |
error-summary | data-count | String(summaryErrorNames.length) |
error-summary | data-state | 'invalid' | 'idle' |
error-summary-item | data-invalid | ''(条件成立时才出现) |
error-summary-item | data-pressed | ''(条件成立时才出现) |
submit-trigger | data-disabled | ''(条件成立时才出现) |
submit-trigger | data-loading | ''(条件成立时才出现) |
submit-trigger | data-pressed | ''(条件成立时才出现) |
submit-trigger | data-xh-action-control | '' |
submit-trigger | data-xh-action-display | 'always' |
submit-trigger | data-xh-action-profile | 'text' |
submit-trigger | data-xh-action-size | 'md' |
submit-trigger | data-xh-action-variant | 'solid' |
submit-trigger | data-xh-ink-surface | '' |
submit-trigger | data-xh-loading-ring | 'overlay' |
reset-trigger | data-disabled | ''(条件成立时才出现) |
reset-trigger | data-pressed | ''(条件成立时才出现) |
reset-trigger | data-xh-action-control | '' |
reset-trigger | data-xh-action-display | 'always' |
reset-trigger | data-xh-action-profile | 'text' |
reset-trigger | data-xh-action-size | 'md' |
reset-trigger | data-xh-action-variant | 'outline' |
CSS 变量
本组件公开覆盖槽由独立皮肤的实际消费位生成;默认来源、作用部件和状态均与 CSS 同源。
| 变量 | 部件 | CSS 属性 | 状态 | 默认来源 | 说明 |
|---|---|---|---|---|---|
--xh-form-field-gap | field-group | gap | default | --xh-space-1 | form 的 field-group 部件 gap 覆盖槽。 |
--xh-form-gap | root | column-gapmargin-block-end | layout=gridwhere(:not(:last-child)where([data-scope='form'][data-part='root']) | --xh-space-5 | form 的 root 部件 column-gap、margin-block-end 覆盖槽。 |
--xh-form-inline-gap | root | column-gap | layout=inline | --xh-space-6 | form 的 root 部件 column-gap 覆盖槽。 |
--xh-form-inline-row-gap | root | margin-block-end | layout=inlinewhere(:not(:last-child)where([data-scope='form'][data-part='root']) | --xh-space-2 | form 的 root 部件 margin-block-end 覆盖槽。 |
--xh-form-label-w | root | grid-template-columns | layout=horizontal | calc(100% * 5 / 24) | form 的 root 部件 grid-template-columns 覆盖槽。 |
--xh-form-loading-duration | submit-trigger | animation | xh-loading-ring | --xh-motion-loop-spin | form 的 submit-trigger 部件 animation 覆盖槽。 |
--xh-form-submit-bg | submit-trigger | --xh-ink-surfacebackground-color | defaultxh-ink-surface | --xh-_action-variant-bg-rest | form 的 submit-trigger 部件 --xh-ink-surface、background-color 覆盖槽。 |
--xh-form-submit-bg-active | submit-trigger | background-color | disabledis(:active, [data-pressed])loadingnot([data-disabled])not([data-loading])pressed | --xh-_action-variant-bg-pressed | form 的 submit-trigger 部件 background-color 覆盖槽。 |
--xh-form-submit-bg-hover | submit-trigger | background-color | disabledhoverloadingnot([data-disabled])not([data-loading]) | --xh-_action-variant-bg-hover | form 的 submit-trigger 部件 background-color 覆盖槽。 |
--xh-form-submit-border | submit-trigger | border | default | --xh-_action-variant-border-rest | form 的 submit-trigger 部件 border 覆盖槽。 |
--xh-form-submit-border-active | submit-trigger | border-color | disabledis(:active, [data-pressed])loadingnot([data-disabled])not([data-loading])pressed | --xh-_action-variant-border-pressed | form 的 submit-trigger 部件 border-color 覆盖槽。 |
--xh-form-submit-border-hover | submit-trigger | border-color | disabledhoverloadingnot([data-disabled])not([data-loading]) | --xh-_action-variant-border-hover | form 的 submit-trigger 部件 border-color 覆盖槽。 |
--xh-form-submit-fg | submit-trigger | borderborder-colorcolor | @media (prefers-reduced-motion: reduce)@media printdefaultdisabledhoveris(:active, [data-pressed])loadingmotion=reducenot([data-disabled])not([data-loading])pressedwhere([data-motion='reduce'])xh-loading-ringxh-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-shadow | submit-trigger | box-shadow | defaultdisabledhoveris(:active, [data-pressed])loadingnot([data-disabled])not([data-loading])pressed | --xh-_highlight-brand | form 的 submit-trigger 部件 box-shadow 覆盖槽。 |
--xh-form-summary-bg | error-summary | background | default | --xh-bg-surface | form 的 error-summary 部件 background 覆盖槽。 |
--xh-form-summary-border | error-summary | border | default | --xh-border-invalid | form 的 error-summary 部件 border 覆盖槽。 |
--xh-form-summary-fg | error-summary | color | default | --xh-fg-danger | form 的 error-summary 部件 color 覆盖槽。 |
--xh-form-summary-font-size | error-summary | font-size | default | --xh-text-body-size | form 的 error-summary 部件 font-size 覆盖槽。 |
--xh-form-summary-gap | error-summary | gap | default | --xh-space-1_5 | form 的 error-summary 部件 gap 覆盖槽。 |
--xh-form-summary-item-bg | error-summary-item | background | default | transparent | form 的 error-summary-item 部件 background 覆盖槽。 |
--xh-form-summary-item-bg-hover | error-summary-item | background | hover | --xh-bg-subtle | form 的 error-summary-item 部件 background 覆盖槽。 |
--xh-form-summary-item-bg-pressed | error-summary-item | background | is(:active, [data-pressed])pressed | --xh-bg-subtle-hover | form 的 error-summary-item 部件 background 覆盖槽。 |
--xh-form-summary-item-fg-hover | error-summary-item | color | hoveris(:active, [data-pressed])pressed | --xh-fg-danger-hover | form 的 error-summary-item 部件 color 覆盖槽。 |
--xh-form-summary-item-font-size | error-summary-item | font-size | default | --xh-text-secondary-size | form 的 error-summary-item 部件 font-size 覆盖槽。 |
--xh-form-summary-item-px | error-summary-item | padding-inline | default | --xh-space-1 | form 的 error-summary-item 部件 padding-inline 覆盖槽。 |
--xh-form-summary-item-radius | error-summary-item | border-radius | default | --xh-shape-control | form 的 error-summary-item 部件 border-radius 覆盖槽。 |
--xh-form-summary-item-underline-offset | error-summary-item | text-underline-offset | default | --xh-space-0_5 | form 的 error-summary-item 部件 text-underline-offset 覆盖槽。 |
--xh-form-summary-px | error-summary | padding-inline | default | --xh-control-px-md | form 的 error-summary 部件 padding-inline 覆盖槽。 |
--xh-form-summary-py | error-summary | padding-block | default | --xh-space-3 | form 的 error-summary 部件 padding-block 覆盖槽。 |
--xh-form-summary-radius | error-summary | border-radius | default | --xh-shape-surface | form 的 error-summary 部件 border-radius 覆盖槽。 |
--xh-form-summary-shadow | error-summary | box-shadow | default | none | form 的 error-summary 部件 box-shadow 覆盖槽。 |
--xh-form-trigger-bg | reset-trigger | --xh-ink-surfacebackground-color | defaultxh-ink-surface | --xh-_action-variant-bg-rest | form 的 reset-trigger 部件 --xh-ink-surface、background-color 覆盖槽。 |
--xh-form-trigger-bg-active | reset-trigger | background-color | disabledis(:active, [data-pressed])loadingnot([data-disabled])not([data-loading])pressed | --xh-_action-variant-bg-pressed | form 的 reset-trigger 部件 background-color 覆盖槽。 |
--xh-form-trigger-bg-disabled | reset-triggersubmit-trigger | --xh-ink-surfacebackground-color | disabledxh-ink-surface | --xh-_action-variant-bg-disabled | form 的 reset-trigger、submit-trigger 部件 --xh-ink-surface、background-color 覆盖槽。 |
--xh-form-trigger-bg-hover | reset-trigger | background-color | disabledhoverloadingnot([data-disabled])not([data-loading]) | --xh-_action-variant-bg-hover | form 的 reset-trigger 部件 background-color 覆盖槽。 |
--xh-form-trigger-border | reset-trigger | border | default | --xh-_action-variant-border-rest | form 的 reset-trigger 部件 border 覆盖槽。 |
--xh-form-trigger-border-disabled | reset-triggersubmit-trigger | border-color | disabled | --xh-_action-variant-border-disabled | form 的 reset-trigger、submit-trigger 部件 border-color 覆盖槽。 |
--xh-form-trigger-border-hover | reset-trigger | border-color | disabledhoverloadingnot([data-disabled])not([data-loading]) | --xh-_action-variant-border-hover | form 的 reset-trigger 部件 border-color 覆盖槽。 |
--xh-form-trigger-fg | reset-trigger | color | defaultdisabledhoveris(:active, [data-pressed])loadingnot([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-size | reset-triggersubmit-trigger | font-size | default | --xh-text-body-size | form 的 reset-trigger、submit-trigger 部件 font-size 覆盖槽。 |
--xh-form-trigger-h | reset-triggersubmit-trigger | block-size | default | --xh-_action-profile-visual-size | form 的 reset-trigger、submit-trigger 部件 block-size 覆盖槽。 |
--xh-form-trigger-px | reset-triggersubmit-trigger | padding-inline | default | --xh-_action-profile-padding-inline | form 的 reset-trigger、submit-trigger 部件 padding-inline 覆盖槽。 |
--xh-form-trigger-radius | reset-triggersubmit-trigger | border-radius | default | --xh-shape-control | form 的 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" 下自动镜像。
