Yeti 组件系统指南:三大 contenteditable="false">【免费下载链接】yetiA CSS-first, native, zero-build layout and styling framework for web designers.
项目地址: https://gitcode.com/gh_mirrors/fo/yeti导读
docs/guides/components.md是 Yeti(CSS-first、零构建的原生布局与样式框架)中面向组件的权威指南:它定义了 button、badge、card、field、table、seam 等组件的统一配置词汇——data-variant、data-emphasis、data-size三个属性,说明如何让浏览器与 ARIA 原生状态(hover、focus、disabled、pressed、busy、checked、invalid)直接驱动外观而无需一行脚本,并演示了表单、卡片、表格、seam 的实战用法。读完本文,你将掌握 Yeti 组件的完整属性表、原生状态映射、可复制的表单/卡片/表格/seam 代码,以及如何仅通过设置 Token 完成整套组件的换肤。
组件哲学:布局管排列,组件管面孔
在 Yeti 中,布局(layouts)负责排列子元素、管理元素之间的间距;而组件(components)"有面孔"(have a face)——它们负责视觉表达。仓库在 src/components/ 下共提供八个组件:affix、badge、button、buttons、card、field、seam、table,与 docs/guides/layouts.md 介绍的十七种布局分工明确。
这张面孔在设计上是刻意安静的:平面表面、统一的一像素边框宽度、除非你显式请求否则没有阴影。更关键的设计约束是——组件绘制的每一个视觉值都来自 Token。因此,改变主题只需要改 Token,不需要改动标记(markup)中的任何一个 class 或 selector。
三大属性:data-variant、data-emphasis、data-size
组件的形状来自标记与 manifest,而外观来自三个data-*属性——只要属性适用,无论哪个组件,名字都完全相同:
| 属性 | 取值 | 被谁读取 |
|---|---|---|
data-variant | primary,secondary,success,warning,alert,neutral | button、badge、card、field |
data-emphasis | high,medium,low | button、badge |
data-size | sm,md,lg | button、badge、field、table、seam |
三个属性的语义各不相同:
data-variant从调色板选一个色相。组件读取该色相色阶(ladder)上的不同台阶来作为填充、边框或文字色。从源码看,这一机制在 src/components/button/button.css 中以--_yeti-variant、--_yeti-variant-subtle、--_yeti-variant-strong、--_yeti-variant-text、--_yeti-on-variant等自定义属性实现:按钮默认用--yeti-color-primary一族的颜色。data-emphasis决定色阶露出多少:high是实心填充,medium是描边或浅色调,low则只用文字色表现色相。在 button.css 中:high(默认)是实心background-color: var(--_yeti-variant);medium把背景设为透明、文字用--_yeti-variant-text;low连边框也透明,只留文字。badge 的默认外观就是subtle浅色调,data-emphasis="high"转为实心填充(见 src/components/badge/badge.css)。data-size同步缩放文字与内边距,一次一个台阶,因此任何尺寸下都不会显得"拉伸变形"。每个组件的默认尺寸也是通过同样的机制设定的:data-size未出现时,源码用.name:not([data-size])选择器把--_yeti-size-text与--_yeti-size-space落到md台阶(如 button.css、src/components/field/field.css、src/components/table/table.css 中的默认声明)。
未携带属性的组件使用自己的默认值,而默认值设定的方式与其他组件完全一致:.name:not([data-size])之类的否定选择器。例如按钮默认是primary变体、md尺寸、high强调度——这解释了一个最小的<a class="button">为什么直接就可用。
状态即原生:浏览器与 ARIA 自带的状态
八个组件中没有一个发明自己的状态属性。用户看到的每一种状态,都是浏览器或 ARIA 已经认识的状态,因此当底层元素进入该状态的那一刻外观就自动生效,无需脚本去设置 class。完整映射如下:
| 状态 | 来源 |
|---|---|
| Hover | :hover—— 按钮的填充加深;表格行在data-hover下出现浅色调。 |
| Focus | :focus-visible—— 与 base 层给所有可聚焦元素的焦点环相同;没有组件移除它。 |
| Disabled | :disabled,或链接充当按钮时的[aria-disabled="true"]。 |
| Pressed | 切换按钮上的[aria-pressed="true"]。 |
| Busy | [aria-busy="true"],通常在按钮等待请求时与aria-disabled="true"一起设置。 |
| Checked | :checked—— 复选框或单选按钮被重绘为 field 的data-variant颜色。 |
| Invalid | :user-invalid,或服务端往返校验失败后的[aria-invalid="true"]。 |
规则很简单:设置状态真正对应的属性——aria-pressed、required、disabled——外观就会自动跟随,除此之外无需任何设置。
源码层面的实现细节值得一提。在 button.css 中:
- 实心按钮的
:hover/:active会把背景、边框、文字切到--_yeti-variant-strong(更深的色阶台阶),并且带有基于--yeti-duration-fast与--yeti-ease的颜色过渡动画; :disabled与[aria-disabled="true"]统一应用--yeti-opacity-muted透明度并改为cursor: not-allowed;[aria-busy="true"]的规则刻意放在 disabled 规则之后(源码注释明确说明),这样忙碌中的按钮(它同时带aria-disabled,因为请求期间不可再按)保持cursor: progress而不是禁用态的not-allowed。
测试也在验证这些行为。test/browser/components/button.spec.js 中:hover 后背景色变化(且等待过渡动画结束后断言)、焦点环只在键盘聚焦时出现(outline-style从none变为solid)、按下的medium/low切换按钮在 hover 时保持按下态外观,还有 light/dark 两种配色下的 AA 对比度与 axe 无无障碍违规断言。
表单,无需 JavaScript:field 与 affix
field把一个 label 与它的控件配成一对,并额外提供 hint(提示)与 error(错误)两个可由控件自行触发的元素:
- label 的
for必须与控件的id匹配;仓库的验证器(validator)会拒绝不匹配的示例。 - 错误信息默认隐藏,直到控件处于 invalid且被触碰(
:user-invalid),或者aria-invalid="true"标记了服务端发现的失败——所以空表单不会在加载时一片红,也不需要任何脚本。 required控件会在 label 后获得一个标记,但只是装饰;真正被屏幕阅读器播报的是required属性本身。affix给控件附加前缀或后缀(单位、符号或按钮),尺寸取自外层的 field 而非自己的属性。
一个完整可复制的示例(来自原文档):
<form class="stack"><article class="card"><div class="scroller" role="region" aria-label="Quarterly results" tabindex="0"> <table class="table"><section class="seam" contenteditable="false">【免费下载链接】yetiA CSS-first, native, zero-build layout and styling framework for web designers.
项目地址: https://gitcode.com/gh_mirrors/fo/yeti创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考