Yeti 框架 Container 布局:把容器查询的测量基准从视口收回到盒子自身
2026/9/19 12:32:00 网站建设 项目流程

Yeti 框架 Container 布局:把容器查询的测量基准从视口收回到盒子自身

【免费下载链接】yetiA CSS-first, native, zero-build layout and styling framework for web designers.项目地址: https://gitcode.com/gh_mirrors/fo/yeti

本篇指南讲解 Yeti 中container布局类的完整用法与底层原理:它只做一件事——把自身的盒子变成容器查询(Container Query)的测量对象,让内部的子元素可以响应「这个盒子的宽度」而非「浏览器视口的宽度」。读完本文你将掌握:何时该用container包裹内容、如何在自有样式表中给容器命名并编写@container查询、以及它与其他会自我查询的 Yeti 布局(gridbreakouttimeline)之间的边界划分。所有结论均可在 src/layouts/container/ 的源码、manifest.json 与 test/browser/layouts/container.spec.js 中验证。

Container 是什么

container是 Yeti 的十七种布局之一(完整清单见 布局指南),它的作用是:

让自身的盒子成为容器查询的测量对象,使盒子内部的内容能够响应盒子的宽度,而不是视口的宽度。

它与stackcluster等会排列子元素的布局不同:container本身不排列任何东西。它存在的唯一意义,是给外部的查询条件提供一个可测量的宽度边界。这一点在其源码注释中写得非常明确(container.css):

/* Container: the box a container query measures. It arranges nothing; it exists so that whatever is inside can respond to its width rather than the viewport's. Name it in your own CSS when a query must target it. */ @layer yeti.layouts { .container { container-type: inline-size; } }

快速上手示例

在 HTML 中,只需把任何内容包进带container类的盒子即可:

<div class="container"> <nav class="cluster" aria-label="Site"> <a href="#">Home</a> <a href="#">Docs</a> </nav> </div>

这个示例与 example.html 中展示的用法一致:外层是container,内层是一个cluster布局的导航。此时,container的内部cluster可以依据「这个容器的宽度」而不是「窗口宽度」改变自身形态(例如窄时从横排折成竖排,见下文命名容器一节)。

何时使用它

文档给出的判断标准非常精确:

围绕任何应当「按自身宽度改变形态」、且自身又无法测量自己盒子的东西来使用。

为什么需要额外一层盒子?关键限制在于:布局可以查询自身并改变子元素,但没有任何东西能从自身查询的内部改变自己的轨道。例如grid可以依据自身宽度决定列数,但grid自身无法在查询中重排自己——因为查询条件依赖的正是它自己的盒子。解决方式就是:

  1. 把该元素放进一个container
  2. 查询这个container的宽度;
  3. 在查询内部改变该元素的子元素或形态。

用 CSS 术语说,这相当于「测量的盒子」与「被重新布局的盒子」被分离:container负责提供测量基准,真正改变形态的是它内部的元素。

工作原理:只有一行声明

从 container.css 可以看到,整个实现只有一行核心声明:

.container { container-type: inline-size; }
  • container-type: inline-size声明该盒子是一个「尺寸容器」(size container),使内部元素能够使用cqw容器单位,并成为@container查询的评估基准;
  • 除此之外再无任何规则——不设置内边距、不约束宽度、不参与排列。

它的声明被放置在级联层yeti.layouts中(层顺序见 layers.css:yeti.reset, yeti.base, yeti.layouts, yeti.components, yeti.utilities)。这也意味着,用户自己的非分层 CSS 默认会覆盖它,这与 Yeti 整体「以用户 CSS 为准」的设计一致。

源码中的同类用法

container-type: inline-size@container的组合在 Yeti 内部被广泛使用,可以佐证这一机制的通用性:

  • grid.css:grid在自身声明container-type: inline-size,然后通过一串@container (inline-size >= 32rem)等条件逐步增加列数,实现data-columnsdata-fold的自适应;
  • breakout.css 与 timeline.css:同样自我声明容器类型并使用@container
  • card.css:组件内部使用container-type: inline-size并在@container (inline-size < 22rem)下调整布局。

注意其中的细节:查询阈值直接写成数字(如22rem),因为容器条件无法读取 CSS 变量(token)——这是 Yeti 设计上的一个硬约束,在 layouts.md 中被明确说明。

按名称定位容器:container-name@container

container类本身不暴露任何属性(见下文),但如果你希望在自有 CSS 中精确地定位某个容器,需要给它命名。文档特别强调:命名只能来自样式表,不能来自 HTML 属性,即不存在data-name之类的写法。正确做法是在你的样式表中追加一条规则:

.container.sidebar-slot { container-name: slot; } @container slot (inline-size < 30rem) { .cluster { flex-direction: column; } }

这里:

  • .container.sidebar-slot通过额外类名sidebar-slot把该容器命名为slotcontainer-name属性);
  • 随后@container slot (inline-size < 30rem)表示:当名为slot的容器内联尺寸小于30rem时,将其中的.cluster从横排切换为竖排。

与「自我查询」布局的分工

文档明确划出了一条边界,避免误用:

Yeti 中会自我查询的布局(使用data-foldgrid、带data-notebreakouttimeline不需要container包裹——它们已经把自己的盒子当作测量对象。

因此container的真正适用对象是两类场景:

  1. 你自己的自定义查询:需要在自有 CSS 中编写@container规则;
  2. 必须重排自身的组件:一个组件需要根据自身所处的盒子宽度改变自己的布局,而它无法通过内部查询改变自己的轨道时,就用container包一层再查询。

配置项:属性、子元素与 Token

按照 manifest.json 的声明,container是 Yeti 中配置面最小的一类布局:

配置项内容
属性(attributes)无。完全通过子元素与 token 配置
类变体(classes)
子元素(children)> *,至少 1 个,任意元素。容器自身不排列任何内容
Token无公开 token

这与其他需要data-gapdata-width等属性配置的布局(如stacksidebar)形成鲜明对比——container的整个公共 API 就是那个类名本身。

无障碍

manifest.json 的a11y字段指出:该组件纯结构性的(purely structural),不要求任何 ARIA 属性,也不涉及键盘交互。测试 container.spec.js 中通过 axe 检查确认其没有任何无障碍违规项。

浏览器支持

场景支持方式
无守卫直接使用容器尺寸查询(container size queries),即container-type: inline-size@container
@supports守卫下无——没有任何需要额外降级处理的规则

也就是说,该组件依赖现代浏览器原生的容器查询能力,不附带任何 polyfill 或回退逻辑。

JavaScript:不需要

container是纯 CSS 组件,不包含任何 JavaScript(manifest 中js字段为null)。引入方式与 Yeti 其余部分一致:通过src/yeti.css或按需引入对应样式文件即可。

用测试验证行为

test/browser/layouts/container.spec.js 中的用例精确验证了本文所述的核心语义,可作为理解与复用的参照:

  1. 查询按容器宽度触发,而非视口:固定视口宽度为1000时,#a#b(位于container内的cluster子项)处于同一行;将舞台缩到400后,#b落到#a之下——查询因容器变窄而触发。
  2. 仅改变视口不会触发查询:容器宽度不变,仅把视口从1000改为500,两个子项依然保持同行——证明测量基准是容器盒子而非窗口。
  3. 无无障碍违规:axe 扫描返回零违规。

对应的测试夹具在 test/browser/fixtures/layouts/container.html:一个#probe.container内嵌.cluster,并在样式表中用@container (inline-size < 30rem)切换.cluster的方向——与文档示例中的写法一致。

关于命名的一点历史

文档「Why this name」一节说明:container的含义是「它包含内容」,并且它正是容器查询要测量的东西。命名上与 Foundation 6 有直接对照:Foundation 6 的.grid-container承担的是「页面栏宽」职责,而那个职责在 Yeti 中由center承担(center将一列内容水平居中并约束最大宽度)。换言之,不要在 Yeti 中把container当页面列宽容器用——那是center的工作;container只负责提供可查询的盒子。

小结

  • container的完整实现只有一行:container-type: inline-size,且不参与任何排列;
  • 用它的时机:内容需要「按自身盒子宽度改变形态」且无法自我查询时,包一层再查询;
  • 命名只能写在样式表里(container-name),配合@container <name> (inline-size < 30rem)使用;
  • 无属性、无 token、无 JavaScript、纯结构性、无无障碍负担;
  • 依赖原生容器尺寸查询,无@supports降级;
  • 自 Yeti 7.0.0 起可用;内部布局grid/breakout/timeline已自我查询,无需container包裹。

【免费下载链接】yetiA CSS-first, native, zero-build layout and styling framework for web designers.项目地址: https://gitcode.com/gh_mirrors/fo/yeti

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询