表单字段
表单字段把一个控件的常驻标签、填写要求、说明与局部错误组织成一个完整输入单元,不额外制造第二套交互模型或无意义的无障碍分组。
安装 Terminal
npm install @a3s-lab/ui@0.4.0示例 index.html入口 main.js
<form data-workspace-name-form novalidate>
<div class="field">
<label for="workspace-name">
工作区显示名称 <span aria-hidden="true">必填</span>
</label>
<input
id="workspace-name"
name="workspaceName"
type="text"
value="智能体运行时"
dir="auto"
minlength="2"
maxlength="40"
autocomplete="organization"
aria-describedby="workspace-name-help"
required
>
<p id="workspace-name-help" data-field-description>用于导航、邀请和审计记录,长度为 2 到 40 个字符。</p>
<p id="workspace-name-error" data-field-message role="alert" hidden>请输入 2 到 40 个字符的工作区名称。</p>
</div>
<button class="btn" type="submit">保存名称</button>
</form>
<script type="module">
const form = document.querySelector("[data-workspace-name-form]");
const input = form.querySelector("#workspace-name");
const field = input.closest(".field");
const error = form.querySelector("#workspace-name-error");
let composing = false;
const setInvalid = (invalid) => {
if (invalid) {
field.dataset.invalid = "true";
input.setAttribute("aria-invalid", "true");
} else {
field.removeAttribute("data-invalid");
input.removeAttribute("aria-invalid");
}
input.setAttribute(
"aria-describedby",
invalid
? "workspace-name-help workspace-name-error"
: "workspace-name-help",
);
error.hidden = !invalid;
};
form.addEventListener("submit", (event) => {
event.preventDefault();
if (composing) {
input.focus();
return;
}
const length = input.value.trim().length;
const invalid = length < 2 || length > 40;
setInvalid(invalid);
if (invalid) input.focus();
});
input.addEventListener("compositionstart", () => {
composing = true;
});
input.addEventListener("compositionend", () => {
composing = false;
});
input.addEventListener("input", (event) => {
const length = input.value.trim().length;
if (!event.isComposing && length >= 2 && length <= 40) {
setInvalid(false);
}
});
</script>import "@a3s-lab/ui/a3s.css";
import "@a3s-lab/ui/all";
用法
导入 Tailwind 和一个完整的 A3S UI 样式包:
@import "tailwindcss" ;
@import "@a3s-lab/ui/a3s.css" ;
按组件加载时,依次导入基础样式、Field 结构、所用控件结构和一个视觉样式:
@import "@a3s-lab/ui/base.css" ;
@import "@a3s-lab/ui/components/field.css" ;
@import "@a3s-lab/ui/components/input.css" ;
@import "@a3s-lab/ui/styles/a3s.css" ;
一个控件只使用一个普通的 <div class="field">。原生控件继续负责聚焦、编辑、校验与表单行为,Field 只负责周围的内容关系。
< div class = "field" >
< label for = "workspace-name" >工作区显示名称</ label >
< input
id = "workspace-name"
name = "workspaceName"
dir = "auto"
aria-describedby = "workspace-name-help"
required
/>
< p id = "workspace-name-help" data-field-description >用于导航和邀请。</ p >
< p id = "workspace-name-error" data-field-message role = "alert" hidden >
请输入工作区名称。
</ p >
</ div >
不要给单个 Field 添加 role="group"。它不会改善控件名称或说明,只会在无障碍树中增加一个未命名分组。多个控件共同回答一个问题时,使用原生 <fieldset> 和 <legend>。
参数
状态和变体
空值与已填写都是中性值状态。只有执行校验后才能进入错误状态。禁用和只读是不同的可用性契约,不能合并成同一种淡化外观。
必填和可选
在标签旁只说明一次填写要求。原生 required 是机器可读契约,可见文字则帮助用户在提交前规划填写内容。
<div class="grid w-full gap-5 sm:grid-cols-2">
<div class="field">
<label for="field-required-owner-zh">
工作区所有者
<span
class="ms-auto text-xs font-normal text-muted-foreground"
aria-hidden="true"
>
必填
</span>
</label>
<input
id="field-required-owner-zh"
type="email"
name="ownerEmail"
autocomplete="email"
aria-describedby="field-required-owner-help-zh"
required
/>
<p id="field-required-owner-help-zh" data-field-description>
用于接收账户恢复和账单通知。
</p>
</div>
<div class="field">
<label for="field-optional-reference-zh">
内部参考编号
<span class="ms-auto text-xs font-normal text-muted-foreground">
可选
</span>
</label>
<input
id="field-optional-reference-zh"
name="internalReference"
aria-describedby="field-optional-reference-help-zh"
/>
<p id="field-optional-reference-help-zh" data-field-description>
仅工作区管理员可见。
</p>
</div>
</div>
禁用和只读
禁用会让控件退出焦点与表单提交;只读仍允许聚焦、选择、复制和提交。两种状态都要保留当前值,并解释为什么不能编辑。
<form class="grid w-full gap-5" data-field-availability="zh">
<div class="field" data-disabled="true">
<label for="field-disabled-slug-zh">已归档工作区标识</label>
<input
id="field-disabled-slug-zh"
name="archivedSlug"
value="legacy-runtime"
aria-describedby="field-disabled-slug-reason-zh"
disabled
/>
<p id="field-disabled-slug-reason-zh" data-field-description>
归档标识会保留在审计记录中,但不能参与提交。
</p>
</div>
<div class="field" data-readonly="true">
<label for="field-readonly-owner-zh">组织所有者</label>
<input
id="field-readonly-owner-zh"
name="organizationOwner"
value="owner@example.com"
aria-describedby="field-readonly-owner-reason-zh"
readonly
/>
<p id="field-readonly-owner-reason-zh" data-field-description>
该值仍可选择并参与提交,只有组织转移才能修改。
</p>
</div>
</form>
相关字段
多个控件共同回答一个问题时,使用一个原生 Fieldset。每个子项仍是只包含一个控件的普通 Field,不需要未命名的 role="group" 包装层。
<fieldset class="fieldset" aria-describedby="field-release-window-help-zh">
<legend>发布窗口</legend>
<p id="field-release-window-help-zh">用于安排生产环境交接。</p>
<div class="grid w-full gap-5 sm:grid-cols-2">
<div class="field">
<label for="field-release-date-zh">日期</label>
<input
id="field-release-date-zh"
name="releaseDate"
type="date"
required
/>
</div>
<div class="field">
<label for="field-release-time-zh">本地时间</label>
<input
id="field-release-time-zh"
name="releaseTime"
type="time"
required
/>
</div>
</div>
</fieldset>
响应式方向
只有在标签、说明和控件仍保持清晰关系时才使用响应式方向,语义顺序始终不变。
<div class="fieldset">
<div class="field" data-orientation="responsive">
<section>
<label for="field-retention-zh">成果保留时间</label>
<p id="field-retention-help-zh" data-field-description>
输入任务完成后成果继续可用的天数。
</p>
</section>
<input
id="field-retention-zh"
name="retentionDays"
type="number"
min="1"
max="365"
value="30"
aria-describedby="field-retention-help-zh"
required
/>
</div>
</div>
RTL
在 Field 或祖先节点设置布局方向。邮箱、网址等机器值继续使用原始文本方向。
<div class="field" dir="rtl">
<label for="field-owner-email-rtl">البريد الإلكتروني للمالك</label>
<input
id="field-owner-email-rtl"
name="ownerEmail"
type="email"
value="owner@example.com"
aria-describedby="field-owner-email-help-rtl"
dir="ltr"
/>
<p id="field-owner-email-help-rtl" data-field-description>
يُستخدم لإشعارات الاسترداد وإدارة مساحة العمل.
</p>
</div>
无障碍
保留一个常驻可见标签,并通过 for 与 id 关联。占位文字不能成为唯一名称。
普通说明始终放在 aria-describedby 中。校验失败后追加可见错误 ID,恢复或重置后及时移除。
无效 Field 保留用户尝试的值,把焦点放回可操作控件,用文字说明问题与具体修正方法,不能只显示红色。
单个 Field 不使用 role="group"。只有多个控件共同构成一个问题时才使用 <fieldset> 与 <legend>。
只有值必须离开焦点和提交时才使用 disabled;用户仍需查看、选择、复制或提交值时使用 readonly。
无法预先确定方向的用户输入自由文本使用 dir="auto";邮箱、网址、路径等机器值保持原始文本方向。
输入法组合期间不执行校验、裁剪或提交,等待组合输入结束。
发布前验证长本地化标签和错误、390px 布局、200% 缩放、暗色、RTL、强制颜色、粗指针和屏幕阅读器与浏览器组合。
Field 不负责领域校验、服务端响应、权限、持久化、提交或错误摘要的焦点协调。Form 与宿主应用负责这些工作,同时保持 Field 的局部关系完整。