Skip to content

Input 输入框 ​

YueInput 是一个原生单行 <input>:v-model、尺寸、状态、前后置内容,以及一个真正的清空按钮。它不负责 label、帮助文本和错误文案——那是 YueField 的职责。

每个预览框的右上角都有自己的外观工具条(浅色 / 深色 / Accent / 密度 / 重置),改的是 Token,页面上的每一个示例都会跟着变。

基础输入与 v-model ​

值永远是字符串。基础输入框不会猜 '42' 是不是数字,需要转换时由消费者或将来的类型化字段负责。

基础原生 input 的键盘、输入法和选区行为全部保留。

值是:

尺寸 ​

三档尺寸复用 Button 的控制尺寸契约(ComponentSize),不是输入框自己的一套像素。不写 size 时回落应用级配置。

尺寸sm / md / lg,以及不写 size 时回落到配置。
size高度横向内边距字号前后置图标
sm--input-height-sm--input-padding-inline-sm--input-font-size-sm--input-icon-size-sm
md--input-height-md--input-padding-inline-md--input-font-size-md--input-icon-size-md
lg--input-height-lg--input-padding-inline-lg--input-font-size-lg--input-icon-size-lg

disabled / readonly / invalid ​

三者是三件不同的事,视觉上也必须不同:

三种不可编辑 / 异常状态只读不是禁用;异常也不是禁用。
状态原生行为视觉语义
disabled原生 disabled:不可聚焦、不提交、读屏不当可编辑禁用背景 + 禁用文字 + 不可操作光标
readonly原生 readonly:仍可聚焦、可选中复制、仍会提交更安静的背景,文字保持可读
invalidaria-invalid="true"错误边界

readonly 与 disabled 的区别不是风格问题

只读字段的用途是「看得见、选得中、复制得走,但改不了」——比如订单号。用 disabled 去表达它,会让值变得不可选中、不可复制,也不会随表单提交。两者的对比度门禁也不同:禁用态在 WCAG 里是豁免的,只读态不是,所以只读文字仍然被审计要求达到 4.5:1。

invalid 只表达「校验没过」,不解释原因

它设置 aria-invalid="true" 并切换错误边界,但不渲染任何错误文案。错在哪里、怎么提示,是 YueField 的事——两个组件各写一份错误文案,就有了两个需要同步的真相来源。

异常状态下的焦点优先级见指南。

placeholder 与原生属性 ​

name、autocomplete、inputmode、required、maxlength、minlength、pattern、aria-*、id 都是原生属性透传,不是 Yue 的 Props。它们在内部真正的 <input> 上,所以 label for、表单提交、浏览器校验和自动填充照常工作。

原生属性label 关联的是内部 input,不是外层 div。

我们会用它发送登录链接,不会公开。

下面的表格说明了每个属性最终落在哪个元素上——这是组件的公开 DOM 契约:

属性落在为什么
id、name、autocomplete、inputmode、required、maxlength、minlength、pattern、aria-*内部 <input>它们是表单控件语义;放在 <div> 上等于没写
class、style、data-*外层 .yue-input它们是「整个组件」的身份与测试钩子

prefix / suffix ​

插槽,不绑定任何图标库。装饰图标请自己写 aria-hidden="true";交互控件不要放进装饰 span 里——需要按钮时用 clearable 或自建独立控件。

前后置内容高度、图标尺寸和间距全部来自 Token。
元

插槽只增加装饰:它不会改变值、选区或键盘行为,也不会生成额外的焦点目标。

clearable ​

清空控件是真正的 <button type="button">:可以被 Tab 到达、有可访问名称、有独立 hover 和 focus-visible 样式。清空之后焦点留在输入框里——清空是「重新输入」的开始,不是编辑的结束。

可清空清空后焦点仍在输入框;Tab 可以到达清空按钮。

清空控件不会出现在禁用、只读或空值时:对一个改不了的输入框提供「清空」是在说谎。

无障碍名称 ​

清空按钮的 aria-label 来自 Yue 的 locale catalog(key input.clear),默认英文 Clear。它不是组件 Prop——一条文案一个 Prop 只服务一个组件、要在每个调用点重复,还会把「翻译」变成「改标记」。

同一个字段,三种 locale 来源三个字段的标记完全一样:第一个读页面语言(中文),第二个在一个 locale 子树里读英文,第三个只给了一个地区标签(en-GB),仓库里并没有这个语言包。

上面三个字段的标记完全一样,区别只在于外面那层 LocaleScope 把子树切到了哪个 locale。组件不知道「语言」存在,它只读 t('input.clear');verify:visual 会在真实浏览器里断言三个 aria-label:默认读页面语言,第二个等于 Clear,第三个同样等于 Clear——但第三条是兜底链在起作用:仓库里没有 en-GB 语言包,en-GB 沿 en-GB → en → en-US 回落到默认包,而不是报错或渲染出 key。

provideLocale() 从 @yue-ui/vue/locale 导出,完整契约见国际化指南。

密码 ​

第一版只要求 type="password" 正确透传。明文切换是消费者自己的 suffix 控件——基础输入框不会偷偷注入图标和内部状态。

密码明文切换由消费者组合,不在组件内部。

浅色 / 深色 / Accent ​

输入框的所有颜色都来自 Token,所以切换主题时不需要 JS 参与:--input-background、--input-border-color*、--input-color 和 --input-placeholder-color 各自在浅色与深色下解析成不同值。

同一份标记,三种主题环境用页面顶部的开关切换主题与 Accent;下面这些字段的标记一个字都不用改。

深色模式下 Error 边界会换成更深色阶上的浅红,保证它仍然看得见——这些取值都在 pnpm audit:tokens 的 110 项门禁里(含只读文字、焦点边界、错误边界、前后置文字和清空控件)。断言不只在 Token 层:verify:visual 会在浅色、深色和中性 Accent 三种环境下各量一遍浏览器里真实渲染的结果。

组件不知道主题

YueInput 里没有一行 JS 判断当前是浅色还是深色,也没有 theme Prop。主题是 Token 解析的结果,不是组件的状态——所以服务端渲染不会因为主题不同而产出不同的 HTML。

状态矩阵 ​

主题 × 状态每一格都是一个真实输入框;浅色与深色的对比度都在 verify:visual 里量过。
resting
placeholder
disabled
readonly
invalid
readonly + invalid
clearable
prefix / suffix

键盘焦点不依赖颜色

:focus-visible 会在整个字段外面画一圈焦点环(--input-focus-ring-*),而不是只把边框换个颜色。异常状态叠加焦点时,错误边界保持错误色,焦点提示由环承担——所以键盘用户不会因为聚焦而失去「这里错了」的信息。

窄屏 ​

字段宽度是 100%,并允许在 flex / grid 列里收缩(min-width: 0),所以窄屏只会受容器约束,不会把页面撑出横向滚动。

窄宽度390px 视口下无横向溢出,这一条在 verify:visual 里断言。

RTL ​

dir 属于字段本身而不是它内部的控件:外层才是排布前后置内容的元素。所以 dir 落在 .yue-input 上,内部 <input> 继承它——整块字段一起镜像,而不是只有输入的文字镜像、前后置图标还留在原顺序。

从右到左前缀在最右,清空按钮在输入框左侧。

把 dir 写在祖先容器上也一样:内部的 <input> 会继承方向,外层照旧是排布者。

什么时候不用它 ​

  • 需要 label、帮助文本、错误文案和必填标记 → 等 YueField,不要把表单布局塞回输入框;
  • 需要多行 → YueTextarea,它的行高和 resize 与单行不同;
  • 需要数字增减、日期、下拉、标签输入 → 各自独立组件,不用一个 type 参数全包;
  • 需要数字格式化 → 先做独立的 formatter,再考虑接到输入框上。

选哪一个「不可编辑」状态 ​

这三个状态经常被混用,但它们的原生行为完全不同:

三选一先问「用户还能不能改」,再问「值还要不要提交」。
想问的问题用
用户改错了,需要重新输入invalid
值由系统给出,用户只能看和复制readonly
这个字段当前完全不适用disabled