
Yeti 表单控件 Field 组件完全指南标签、提示与自显现错误的状态驱动表单【免费下载链接】yetiA CSS-first, native, zero-build layout and styling framework for web designers.项目地址: https://gitcode.com/gh_mirrors/fo/yetiYeti 是一个 CSS-first、原生、零构建的布局与样式框架项目说明而field是其中负责一个表单控件及其附属信息的组件它把一个 label、一个原生控件、一段可选 hint 提示和一段错误信息捆绑为一个整体。本文以 docs/field.md 为骨架结合 field.css 源码、manifest.json 声明与 field.spec.js 测试完整讲解 field 的用法、属性、子元素、设计令牌与无障碍细节让你能直接用纯 HTML 类名搭建无需 JavaScript 的校验型表单。Field 是什么一个控件的一切Yeti 的 Field 组件定义非常凝练一个表单控件text、email、number、select、textarea、checkbox、radio、switch、range 等任意一种配上它的标签、可选的帮助文本以及一个当控件无效时自己显现出来的错误信息。它解决的是裸 HTML 表单做不到的三件事label 与控件的关联for↔id配对帮助文本hint的排版与语义挂载错误信息的显示时机——既不在页面加载时全红一片也不需要一行脚本。在 Yeti 的实践里一个表单通常是stack布局下若干 field 与一个 button 的纵向组合field 负责承接 HTML 语义本身无法表达的那部分。最小示例一个带提示与错误的邮箱输入官方文档给出如下最典型的结构example.html 中完全一致div classfield label foremailEmail/label input idemail typeemail required aria-describedbyemail-hint email-error p idemail-hint>.field { display: flex; flex-direction: column; gap: var(--yeti-field-gap); } .field * { margin: 0; } .field :is(label, legend) { font-weight: var(--yeti-weight-strong); font-size: var(--_yeti-size-text); }label 与 legend 使用粗体--yeti-weight-strong字号跟随内部令牌--_yeti-size-text由data-size决定。2. 控件读取控件令牌主题一处改动处处生效文本类控件input 的非特殊类型、select、textarea直接以原生元素配合令牌样式.field :is(input:not(:where([typecheckbox], [typeradio], [typerange], [typecolor], [typefile])), select, textarea) { min-block-size: calc(var(--yeti-control-size) max(0px, var(--_yeti-size-space) - var(--yeti-space-sm))); padding: 0 var(--_yeti-size-space); font: inherit; font-size: var(--_yeti-size-text); color: var(--yeti-color-text); background-color: var(--yeti-control-surface); border: var(--yeti-border-width) solid var(--yeti-control-border); border-radius: var(--yeti-control-radius); transition: border-color var(--yeti-duration-fast) var(--yeti-ease); }这正是文档强调的控件由--yeti-control-*一组令牌统一样式主题只要改了--yeti-control-radius所有输入框的圆角随之改变。例如 soft.css 主题正是通过--yeti-control-radius: var(--yeti-radius-md);一行让控件更圆润。特殊处理还包括textarea额外设置padding-block与min-block-size: 4lh约 4 行文本高度这里用到了lh单位见浏览器支持一节select使用appearance: none去掉原生外观把背景替换为--yeti-control-chevron这个内联 SVG 箭头图像并预留右侧 padding 防止文字压到箭头:focus-visible时边框变为--yeti-color-border-strong键盘焦点清晰可辨。3. 错误显现的时机::user-invalid与aria-invalid错误默认隐藏仅在两种情况下显现.field [data-error] { display: none; ... } .field:has(:user-invalid, [aria-invalidtrue]) [data-error] { display: block; }客户端校验控件:user-invalid命中——即用户已经触碰过控件且值无效email输入nope后失焦即触发错误显现服务端往返服务器返回后你设置aria-invalidtrue错误立即显现无需用户再操作。同时无效控件自身的边框会转为告警色--yeti-color-alert且只红自己的控件.field:has( :user-invalid, [aria-invalidtrue], .affix :is(:user-invalid, [aria-invalidtrue])) :is(input, select, textarea), .field:has( .affix :is(:user-invalid, [aria-invalidtrue])) .affix :is(input, select) { border-color: var(--yeti-color-alert); }测试 field.spec.js 验证了这套行为#email-error初始display: none填入nope并 blur 后变为block且边框色改变而#name-error夹具中通过aria-invalidtrue模拟服务端错误在页面打开时就直接显示。另一个用例专门验证一个无效控件只红自己的控件——给#p-a设aria-invalidtrue后兄弟#p-b的边框保持不变。4. required 的视觉标记.field:has([required]) :is(label, legend)::after { content: *; color: var(--yeti-color-alert-text); }required控件会在 label或 legend后追加一个星号标记颜色为告警文字色。测试通过读取getComputedStyle(..., ::after).content确认它包含*。注意星号只是装饰真正被辅助技术朗读的是required属性本身见无障碍一节。5. checkbox 与 radio自动内联 变体色勾选checkbox 与 radio 使用appearance: none重绘尺寸为1.25em勾选时.field input:is([typecheckbox], [typeradio]):checked { background-color: var(--_yeti-variant); border-color: var(--_yeti-variant); box-shadow: inset 0 0 0 0.2em var(--yeti-control-surface); }即变体色实心中心 表面色内环的视觉效果radio 圆角为 50%。测试会验证勾选后背景色等于--_yeti-variant解析出的颜色、取消勾选后背景回到--yeti-control-surface。布局上checkbox/radio 所在的 field自动变成内联行label 在控件右侧不需要任何属性.field:is([data-inline], :has( input:is([typecheckbox], [typeradio]))) { flex-direction: row; flex-wrap: wrap; align-items: center; gap: var(--yeti-space-xs); }测试用几何断言确认了label 左侧大于控件右侧、二者垂直居中。内联时 hint 与 error 通过flex-basis: 100%换行独占一行。6. fieldset 分组多个控件共享一个 legend当需要给一组选项如一组 radio命名时把field类直接用在fieldset上fieldset classfield legendNotify me by/legend div classfieldinput idn-email typecheckbox namenotify valueemaillabel forn-emailEmail/label/div div classfieldinput idn-sms typecheckbox namenotify valuesmslabel forn-smsText message/label/div p>fieldset.field { padding: var(--yeti-space-sm) var(--yeti-space-md); border: var(--yeti-border-width) solid var(--yeti-control-border); border-radius: var(--yeti-control-radius); } fieldset.field legend { padding-inline: var(--yeti-space-xs); }7. switch一个 checkbox 加上roleswitchcheckbox 加roleswitch就变成开关轨道 滑块打开时滑块滑到末端且轨道取变体色div classfieldinput iddark typecheckbox roleswitchlabel fordarkDark mode/label/div源码实现是inline-size: 2.25em的胶囊轨道--yeti-radius-full用radial-gradient画圆形滑块background-position从0% 50%移到100% 50%完成滑动勾选时背景变体色、滑块环消失box-shadow: none。测试断言了轨道宽度大于高度的 1.5 倍、切换前后背景色与位置均变化。8. range细轨道 圆滑块div classfieldlabel forvolumeVolume/labelinput idvolume typerange min0 max100 value40/divrange 输入本身是一个控件的完整高度block-size: var(--yeti-control-size)保证滑块是一个舒适的点击目标轨道只有0.25em高、圆角胶囊状、颜色为--yeti-control-border滑块为 1.25em 圆形、变体色带边框。CSS 同时书写::-webkit-slider-*与::-moz-range-*两套伪元素。测试指出轨道不会填充到当前值——因为 CSS 读不到控件的值这是文档明确说明的设计取舍。无障碍Accessibility文档对无障碍给出明确要求且这些要求被 validator 与测试双重约束label 的for必须匹配控件的idYeti 的 validator 会拒绝缺少配对的示例把hint 与 error 的 id 放进控件的aria-describedby屏幕阅读器就能随控件朗读帮助文本并在错误出现的瞬间朗读错误服务端发现的错误用aria-invalidtrue呈现required 标记只是装饰辅助技术真正读到的是required属性本身switch 就是带roleswitch的 checkbox它的 label 即开关的名称range 和其他控件一样需要 label当数值不是人能直接说出的内容时应提供aria-valuetext。这些行为在 manifest.json 的a11y.notes中原文记录并在 field.spec.js 中通过 axe 无障碍扫描has no accessibility violations与明暗两种配色方案下的 AA 对比度测试text meets AA in light/dark得到验证。属性Attributes属性类型取值默认说明data-sizeenumsm,md,lgmd缩放控件的高度与文字。data-inlineboolean——把 label 放到控件旁边。checkbox 与 radio 不需要它本身就内联。data-variantenumprimary,secondary,success,warning,alert,neutralprimary勾选后的 checkbox / radio 的颜色。属性背后的机制从源码结构看这些属性不是 field 特有的规则而是复用 attributes.css 中一套通用的布局属性解析data-variant为元素挂载--_yeti-variant等一组变体色阶梯变量取值集合定义在 vocabulary.json 的variant词条中field 通过:not([data-variant])设置默认的 primary 变体所以不会继承父元素的变体data-size解析为两个内部令牌--_yeti-size-text文字步进与--_yeti-size-space间距步进例如lg对应--yeti-text-lg与--yeti-space-mdsm则更紧凑。field.css 中控件的min-block-size与 padding 都读取这两个令牌从而整体缩放data-inline是布尔属性仅用于把 label 排到控件旁边对 checkbox/radio 属冗余写法因为:has()已自动处理。size-control词条位于 vocabulary.json 中值为[sm, md, lg]。子元素Childrenmanifest.json以 selector形式声明了 field 允许的子元素结构每个最多一个 label01 个。labelfor指向控件的 id除非是带 legend 的 fieldset否则必须存在。 legend01 个。当 field 是分组多个控件的 fieldset 时使用。 input01 个。控件。 select01 个。控件。 textarea01 个。控件。 .affix01 个。控件槽位交给 affix 组件——即带附属物的控件或两个控件拼接参见 affix 组件 与 affix.css。 [data-hint]01 个。帮助文本由控件的aria-describedby引用。 [data-error]01 个。错误消息控件无效前保持隐藏。hint 与 error 的样式在源码中分别为--yeti-text-sm字号hint 用--yeti-color-text-muted弱化error 用--yeti-color-alert-text告警色。设计令牌Tokens令牌说明--yeti-field-gaplabel、控件与 hint 之间的间距。--yeti-control-size控件的最小高度。--yeti-control-radius控件的圆角。--yeti-control-border控件静止时的边框。--yeti-control-surface控件的背景。--yeti-control-chevronselect 的箭头图像。--yeti-color-alert无效控件的边框颜色。默认值与主题化这些令牌的默认值定义在 surface.css 与 components.css并汇总于 tokens.json 的control分组--yeti-control-size: 2.5remmd尺寸下按钮或输入的最小块高--yeti-control-radius: var(--yeti-radius-sm)--yeti-control-border: var(--yeti-color-border)--yeti-control-surface: var(--yeti-color-surface)--yeti-control-chevron一个中灰色内联 SVG 箭头主题可为深色控件表面提供更亮的箭头如 tokens.json 中的说明--yeti-field-gap: var(--yeti-space-xs)。--yeti-color-alert定义于 color.css是一个随明暗模式切换的light-dark()颜色浅色下约#af3c3a。正如文档所说主题只要覆盖--yeti-control-radius如 soft.css 改为radius-md即可让全部控件变圆润——这正是控件读取控制令牌设计带来的杠杆效应。内部令牌可能在次要版本间变化以下令牌以下划线开头是内部实现细节不建议在主题中依赖--_yeti-variant--_yeti-on-variant--_yeti-size-text--_yeti-size-space浏览器支持无守卫直接使用:has()、:user-invalid、appearance: none、lh单位supports守卫之后无。这意味着 field 依赖现代浏览器的原生能力:user-invalid是 Chromium 111 / Firefox 88 / Safari 16.5 支持的伪类但换来的是零脚本的校验体验。若目标浏览器较旧需要自行评估降级方案。JavaScript无。该组件纯 CSS 实现CSS only自 7.0.0 版本起可用。错误显隐、开关滑块、range 滑块全部由 CSS 状态与伪元素完成这也是 field 在 field.spec.js 中能通过纯浏览器断言验证无 mock、无注入脚本的原因。小结Yeti 的 field 组件展示了CSS-first表单设计的完整路径用:has():user-invalid把校验状态从控件本身映射到错误文本与边框用appearance: none重绘 checkbox、radio、switch、range用控制令牌统一所有控件的尺寸与圆角再以 validator 强制 label↔id 配对保证无障碍底线。按本文的示例与属性说明你可以直接用语义化 HTML 搭建出无脚本、可访问、可主题化的完整表单。【免费下载链接】yetiA CSS-first, native, zero-build layout and styling framework for web designers.项目地址: https://gitcode.com/gh_mirrors/fo/yeti创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考