Yeti 网格布局 Grid 完全指南用data-min、data-columns、data-fold与data-ranks构建零断点响应式栅格【免费下载链接】yetiA CSS-first, native, zero-build layout and styling framework for web designers.项目地址: https://gitcode.com/gh_mirrors/fo/yeti本篇指南以 Yeti 的 Grid 布局src/layouts/grid为主题深入讲解这套CSS-first、原生、零构建布局框架如何用一组数据属性实现能排几列就排几列的自适应栅格你只声明单元格最小宽度与可选的最大列数浏览器替你决定列数再通过data-fold让列数成倍折叠、用data-ranks让相邻卡片的各个部分逐行对齐。读完本文你将掌握 Yeti Grid 全部五个数据属性的语义、底层 CSS 轨道公式的推导逻辑以及它与 Card 组件、Columns 布局的协作方式并了解 Playwright 测试如何逐条验证这些行为。何时使用 GridGrid 适用于同类事物的集合——卡片、缩略图、团队成员、商品列表等需要排成等宽单元格的场景。它与 Columnssrc/layouts/columns的分工不同Columns 由你指定一个容器阈值决定何时由多列切为单列Grid 则把列数的决定权交给浏览器——由 Grid 决定能容纳多少列而你只需决定一个单元格最窄可以到多少、以及当需要时最多允许多少列。这是 Yeti 中唯一一个列数由内容容器自适应的布局适合栅格状的重复内容而 Columns 面向固定 N 列或单列的段落式布局。两者互补而非互斥src/layouts/grid/manifest.json 中将其类型定义为kind: layout类名grid自7.0.0起提供。工作原理一行轨道公式Grid 的核心只有一条 CSS 轨道声明src/layouts/grid/grid.css.grid { display: grid; gap: var(--_yeti-gap); grid-template-columns: repeat(auto-fit, minmax(min(max(var(--_yeti-min), var(--_yeti-column-cap)), 100%), 1fr)); }把它拆开看repeat(auto-fit, …)自动重复轨道能放几个就放几个放不下的折行minmax(min, 1fr)每个轨道的最小值是min最大值是1fr等分剩余空间min(max(var(--_yeti-min), var(--_yeti-column-cap)), 100%)真正的最小宽度取--_yeti-min最小列宽与--_yeti-column-cap列数上限换算出的轨道宽中的较大者但不超过容器宽度的100%——100%兜底保证容器极窄时轨道不会溢出。也就是说轨道最小宽度 max(最小列宽, 容器宽度 ÷ 最大列数)。这正是文档中track list isrepeat(auto-fit, minmax(min, 1fr))其中min是data-min与容器按data-columns均分之值中的较大者的代码级对应。三个私有变量各自的默认值同样在 grid.css 中.grid:not([data-gap]) { --_yeti-gap: var(--yeti-space-md); } .grid:not([data-min]) { --_yeti-min: var(--yeti-width-xs); } .grid:not([data-columns]) { --_yeti-column-cap: 0px; }即间距默认md正文文字大小最小列宽默认xs16rem无data-columns时列数上限为 0不设上限。这套属性缺席时由布局自身提供默认值、属性在场时由 src/layouts/attributes.css 统一映射的设计保证了嵌套布局不会继承父级的私有变量——每个data-*只在本元素设置一次私有属性详见 attributes.css 头部的注释。数据属性速查表以下参数表来自 src/layouts/grid/manifest.json是 Yeti 对每个属性的权威定义属性类型 / 词汇表默认值含义data-min枚举width-or-nonexs一列最窄可以到多少none表示只有data-columns决定列数data-gap枚举gapmd列与行之间的间距data-columns枚举columns无允许的最大列数容器放不下时列数减少data-minnone时列数精确data-fold布尔无随着网格变窄列数成倍减半而非逐列递减data-ranks枚举ranks无让各子元素的部分跨行对齐值是子元素拥有几个部分其中data-min的可选值来自 schema/vocabulary.json 的width-or-nonenone, xs, sm, md, lg, xl, 2xldata-columns取columns1–6data-gap取gap从none到3xl含 20 余个流动型配对如sm-mddata-ranks取ranks2–6。每个取值在 attributes.css 中都有一条对应的规则例如[data-minnone] { --_yeti-min: 0px; } [data-minxs] { --_yeti-min: var(--yeti-width-xs); } ... [data-columns4] { --_yeti-column-cap: calc((100% - 3 * var(--_yeti-gap)) / 4); }注意data-columnsN的换算公式是calc((100% - (N-1) × gap) / N)——它把 N-1 个间距从容器宽度里扣掉再均分因此容器均分 N 份是严格意义上的轨道宽度下限。列数如何被决定当容器足够宽时auto-fit按最小列宽--_yeti-min尽可能多地排轨道一旦容器宽度超过最小列宽 × N--_yeti-column-cap就会取代最小列宽成为轨道下限于是列数永远不会超过 N而当容器窄到连 N 列、每列都达不到最小列宽时auto-fit会自动减少列数——因此更少列依然会发生只是以最小列宽为底线。固定列数data-minnonedata-columns文档给出了一组精确四列的写法div classgrid>.grid[data-minnone]:not([data-columns]) { --_yeti-column-cap: 100%; }把列数上限换算为容器宽度本身轨道最小宽度即容器宽度——结果就是单列全宽而不是失控的多轨道。测试 test/browser/layouts/grid.spec.js 中的两条用例完整覆盖了这一行为test(data-minnone with>.grid[data-fold] { container-type: inline-size; grid-template-columns: repeat(12, minmax(0, 1fr)); } .grid[data-fold] * { grid-column: span 12; }网格自己变成 12 轨道的容器每个子元素默认跨满 12 轨单列随后以container (inline-size …)条件按当前宽度把子元素的grid-column改为span 6两列、span 4三列、span 3四列、span 2六列。阈值 宽度 token 的默认值 × 列数。以data-minxs、data-columns4为例grid.css 第 33-41 行container (inline-size 64rem) { .grid[data-fold]:is(:not([data-min]), [data-minxs])[data-columns4] * { grid-column: span 3; } }xs 的默认值是 16remsrc/tokens/tokens.json 中--yeti-width-xs的默认值四列即 64rem于是内容盒content box至少四个 xs 宽度时四列并排降到 32rem2 × xs时两列两行更低则单列。宽度 token 全集为xs 16rem、sm 24、md 32、lg 48、xl 64、2xl 80见 schema/tokens.schema.json 与 tokens.json 的width分组grid.css 为每种 token 都预置了全套容器查询。因为阈值是token 默认值 × 列数的字面量主题若改动了宽度 token移动的是 token 本身、而不是折叠点——这正符合主题改 token 值、不改行为语义的设计哲学。源码注释也指出阈值是字面的literal因为容器查询条件无法读取 CSS 变量。与嵌套 Columns 的对比文档给出了无容器查询时代的近似物——用两层 Columns 嵌套出同样的 4→2→1 步进div classcolumns>.grid[data-ranks] * { display: grid; grid-template-rows: subgrid; grid-row: span var(--_yeti-ranks); row-gap: var(--_yeti-gap); }--_yeti-ranks由 attributes.css 的[data-ranks2] … [data-ranks6]映射取值范围 2–6。测试 grid.spec.js 中data-ranks lines up the parts of neighbours用真实 DOM 验证即使某个卡片的标题换行两行、段落冗长见 test/browser/fixtures/layouts/grid.html 中#ranked的三个 item所有卡片的h2顶边、p顶边仍各自处于同一水平线。与 Card 组件的协作subgrid 不能同时是容器一个关键限制排行栅格里的子元素不能再作为尺寸容器size container因为容器查询不能作用于自身而 subgrid 的轨道尺寸由祖先共享——containment 禁止这种共享。因此 Card 组件在排行栅格内会主动关掉自己的容器特性src/components/card/card.css.grid[data-ranks] .card { display: grid; grid-template-rows: subgrid; grid-row: span var(--_yeti-ranks); row-gap: var(--yeti-card-gap); container-type: normal; } .grid[data-ranks] .card footer { margin-block-start: 0; grid-row: -2; }这样卡片在排行栅格中图片与页脚与邻居卡片对齐页脚被显式放到最后一行grid-row: -2即使卡片部分较少也能对齐页脚不会切换到自己的缩略图行模式其窄屏容器查询永远不会命中排行卡片。无障碍与语义manifest.json 的a11y字段注明 Grid纯粹是视觉布局无需额外 ARIA 属性或键盘交互若单元格本身是同类事物的列表应当使用列表元素ul/li承载语义。这一点在 example.html 中已有示范——它用ul classgrid … rolelist并且测试专门验证了rolelist时padding-inline-start被重置为 0见rolelist keeps padding reset用例。另外 grid.css 末尾的.grid * { margin: 0; }清除了子元素默认外边距测试children have no margins与has no accessibility violationsaxe 扫描零违规共同保证基础健壮性。为什么叫这个名字文档的说法很直白它就是一个 grid没有别的东西。对 Foundation 6 的读者而言Grid 取代了 Block Griddata-columns4是large-up-4的内蕴intrinsic形式——窄宽度下的收缩由最小列宽处理而不是靠small-up-1 medium-up-2这类断点组合。这正是零断点理念在栅格上的体现没有媒体查询、没有类名阶梯一个属性表达意图浏览器完成全部响应。实战要点小结默认行为div classgrid即获得最小列宽 16rem--yeti-width-xs、间距md、列数不限的自适应栅格封顶列数加data-columnsN1–6列数只减不增无需任何断点精确列数data-minnone与data-columns成对使用单独使用会退化为单列全宽成倍折叠需要 4→2→1 或 6→3→1 的商务感步进时用data-fold阈值随宽度 token 主题自动迁移卡片对齐多张结构不同的卡片并排时用data-ranksN让图片、标题、正文、页脚逐行对齐。所有行为都有对应的 Playwright 测试 与 测试固件 可以对照验证属性的完整权威定义见 manifest.json取值词汇表见 schema/vocabulary.json。【免费下载链接】yetiA CSS-first, native, zero-build layout and styling framework for web designers.项目地址: https://gitcode.com/gh_mirrors/fo/yeti创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考