文本框

这里分两层。input() 和 textarea() 是光秃秃的控件;textField() 和 textareaField() 则把控件包上标签、帮助文本和错误信息,并用 for 和 aria-describedby 把它们连起来。除非你打算自己搭这套排版,否则请用后者。

基础文本框

textField({ label: '姓名', name: 'name', placeholder: 'Ada Lovelace' })

帮助文本

帮助文本通过 aria-describedby 关联,所以屏幕阅读器会把它当成这个字段的一部分来读,而不是后面一段游离的文字。

我们只会用它来通知构建失败。
textField({
  label: '邮箱',
  name: 'email',
  type: 'email',
  help: '我们只会用它来通知构建失败。',
})

必填与错误

一个 error 会把字段标为无效、给边框上色、设置 aria-invalid,并让 aria-describedby 指向那条消息——一个属性,四件事全办了。

这不是一个 URL。
stack({ gap: 'lg' },
  textField({ label: '项目', name: 'project', required: true, value: '' }),
  textField({
    label: '站点',
    name: 'site',
    error: '这不是一个 URL。',
    value: 'sitelo 点 dev',
  }),
)

尺寸

stack({ gap: 'md' },
  textField({ label: '小号', name: 'small', size: 'sm', placeholder: 'sm' }),
  textField({ label: '中号', name: 'medium', size: 'md', placeholder: 'md' }),
  textField({ label: '大号', name: 'large', size: 'lg', placeholder: 'lg' }),
)

前后缀

贴在控件本身上的前缀或后缀,用来放单位和值里固定的那一截。

https://
秒
stack({ gap: 'md' },
  textField({ label: '站点', name: 'url', startAdornment: 'https://', placeholder: 'example.com' }),
  textField({ label: '构建超时', name: 'timeout', endAdornment: '秒', value: '30' }),
)

禁用与只读

stack({ gap: 'md' },
  textField({ label: '已禁用', name: 'disabled', value: '不能编辑', disabled: true }),
  textField({ label: '只读', name: 'readonly', value: 'dist/', readonly: true }),
)

多行

textareaField() 就是同一个字段,只不过里面包的是 <textarea>。它的值是元素内容而不是属性,这一点组件已经替你处理好了。

会出现在搜索结果和社交卡片里。
textareaField({
  label: '描述',
  name: 'description',
  rows: 4,
  help: '会出现在搜索结果和社交卡片里。',
  value: '由 Vite 驱动的零配置静态站点生成。',
})

在表单里

card(
  cardBody(
    stack({ gap: 'md' },
      textField({ label: '姓名', name: 'contact-name', required: true }),
      textField({ label: '邮箱', name: 'contact-email', type: 'email', required: true }),
      textareaField({ label: '留言', name: 'message', rows: 3 }),
    ),
  ),
  cardFooter({ divided: true, style: 'justify-content: flex-end' },
    button({ variant: 'ghost', color: 'neutral' }, '取消'),
    button({ type: 'submit' }, '发送'),
  ),
)

自己搭

field() 就是单独那层外壳——它接受任意控件作为子元素,所以你可以把两个输入放在同一行,或者塞一个本库没有的控件,同时享有一样的标签和错误处理。

一个标签没法同时命名两个控件,所以这里每个输入都需要自己的无障碍名称。那几个 aria-label 就是干这个的:可见标签命名的是这一对,而每个输入说明自己是哪一端。

两端都包含在内。
field({ label: '日期范围', help: '两端都包含在内。' },
  stack({ direction: 'row', gap: 'sm' },
    input({ type: 'date', name: 'from', 'aria-label': '起始' }),
    input({ type: 'date', name: 'to', 'aria-label': '结束' }),
  ),
)

属性

textField 和 textareaField

属性类型默认值说明
labelChild—字段标签。没有 name 时,控件的 id 也由它推导。
namestring—表单字段名;id 由它推导而来。
helpChild—控件下方的提示,用 aria-describedby 关联。
errorChild | false—错误信息。同时会在控件上设置 aria-invalid。
requiredbooleanfalse同时标记标签和控件。
size'sm' | 'md' | 'lg''md'控件高度和文字大小。
typestring'text'任意 input 类型。仅 textField 有效。
startAdornmentChild—贴在控件前面的前缀。仅 textField 有效。
endAdornmentChild—贴在控件后面的后缀。仅 textField 有效。
valuestring | number—初始值。
fieldClassstring—加在外层容器上的类,而不是控件上。

id 是从 name 推导出来的——没有 name 时则从 label 推导——而不是靠一个计数器,所以同一个页面每次构建都会生成一样的 HTML。想覆盖它就传 id。

field

属性类型默认值说明
labelChild—标签文字。
helpChild—控件下方的提示。
errorChild | false—错误信息;同时给外层容器加上无效状态。
requiredbooleanfalse在标签上加必填标记。
forstring—被标注控件的 id。