news 2026/9/19 6:32:39

Yeti 组件系统指南:三大 data-* 属性、原生状态与 Token 皮肤化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Yeti 组件系统指南:三大 data-* 属性、原生状态与 Token 皮肤化

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-variantdata-emphasisdata-size三个属性,说明如何让浏览器与 ARIA 原生状态(hover、focus、disabled、pressed、busy、checked、invalid)直接驱动外观而无需一行脚本,并演示了表单、卡片、表格、seam 的实战用法。读完本文,你将掌握 Yeti 组件的完整属性表、原生状态映射、可复制的表单/卡片/表格/seam 代码,以及如何仅通过设置 Token 完成整套组件的换肤。

组件哲学:布局管排列,组件管面孔

在 Yeti 中,布局(layouts)负责排列子元素、管理元素之间的间距;而组件(components)"有面孔"(have a face)——它们负责视觉表达。仓库在 src/components/ 下共提供八个组件:affixbadgebuttonbuttonscardfieldseamtable,与 docs/guides/layouts.md 介绍的十七种布局分工明确。

这张面孔在设计上是刻意安静的:平面表面、统一的一像素边框宽度、除非你显式请求否则没有阴影。更关键的设计约束是——组件绘制的每一个视觉值都来自 Token。因此,改变主题只需要改 Token,不需要改动标记(markup)中的任何一个 class 或 selector。

三大属性:data-variantdata-emphasisdata-size

组件的形状来自标记与 manifest,而外观来自三个data-*属性——只要属性适用,无论哪个组件,名字都完全相同:

属性取值被谁读取
data-variantprimary,secondary,success,warning,alert,neutralbutton、badge、card、field
data-emphasishigh,medium,lowbutton、badge
data-sizesm,md,lgbutton、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-textlow连边框也透明,只留文字。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-pressedrequireddisabled——外观就会自动跟随,除此之外无需任何设置。

源码层面的实现细节值得一提。在 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-stylenone变为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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 6:32:37

电商主图批量生产工业化:Prompt模板化与自动化质检实践

1. 项目背景&#xff1a;为什么我要把电商主图生产“工业化”先说结论&#xff1a;这件事的起点&#xff0c;不是“AI绘图很酷”&#xff0c;而是“人工做图太痛了”。我手头同时运营着几个不同类目的店铺&#xff0c;SKU加起来几百个&#xff0c;每个月要上新的款式至少在30到…

作者头像 李华
网站建设 2026/9/19 6:32:09

AI资讯聚合系统:从抓取到简报的工程化实践

我无法根据当前输入生成符合要求的博文。原因如下&#xff1a;项目标题为“AI 日报&#xff08;2026年9月11日&#xff09;”&#xff0c;属于未来日期的时效性内容&#xff0c;不具备现实可验证的技术实体、具体功能、可复现操作或真实项目背景&#xff1b;项目正文为空&#…

作者头像 李华
网站建设 2026/9/19 6:30:28

LVS、Keepalived、HAProxy三件套:负载均衡与高可用架构实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 6:29:26

Lobster框架实测:可插拔引擎+持久Agent,让长任务断点续跑成为标配

最近 GitHub 趋势榜上多了个外号特别接地气的项目&#xff0c;大家都喊它"龙虾"&#xff0c;英文代号 Lobster。我一开始是被这个代号吸引点进去的&#xff0c;结果发现它这次的大版本更新把两个我一直念叨的能力做到了框架级别&#xff1a;可插拔引擎&#xff08;En…

作者头像 李华
网站建设 2026/9/19 6:26:53

2026年临沂人力资源咨询公司选型实操指南:避坑与落地验收

2026年&#xff0c;做人力资源管理咨询的临沂老板们普遍都有一种焦虑&#xff1a;到处都能看到"管理咨询""人力资源外包""薪酬绩效落地"的广告&#xff0c;但真到了要掏钱选型的时候&#xff0c;反而不知道该信谁。我这两年接触过不少临沂本地的…

作者头像 李华
网站建设 2026/9/19 6:25:06

快速部署ERC-20代币实战:从合约编写到链上运营全流程指南

快速部署ERC-20代币&#xff0c;以Polkadot生态为例——从合约编写到链上运营的完整经验去年我帮一个Web3项目方搭代币经济模型的时候&#xff0c;核心需求就一句话&#xff1a;“我们要在Polkadot生态里发一个ERC-20代币&#xff0c;越快越好&#xff0c;但要有可运营性。”当…

作者头像 李华