Yeti 的 Media 媒体对象配方:一行 CSS 类实现“图配文“并排布局
2026/9/19 11:16:52 网站建设 项目流程

Yeti 的 Media 媒体对象配方:一行 CSS 类实现"图配文"并排布局

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

本文以 Yeti(一个 CSS-first、零构建的布局与样式框架)中的media配方(recipe)为主线,讲解"图片/视频旁边配几行文字"这一经典 UI 形态的实现原理与实战用法。读完你将掌握:如何用单个.media类快速搭建头像配评论、缩略图配商品简介、头像配人物介绍等布局,理解它与sidebar+frame+stack三个基础布局原语之间的组合关系,并能通过data-widthdata-ratiodata-gapdata-aligndata-side五个属性精确控制其几何表现。

何时使用 media

评论配头像、商品配缩略图、演讲者配证件照——只要是一张图片挨着几行文字的场景,就是这个形态。它的行为很明确:图与文并排展示,文字始终能保住至少一半的宽度;一旦放不下,就自动上下堆叠

使用上有一条重要的源顺序(source order)约定:

  • 图片放在第一位 → 图片在左、文字在右;
  • 图片放在最后一位 → 文字在左、图片在右;
  • 阅读顺序(DOM 顺序)始终与视觉顺序保持一致,键盘与屏幕阅读器用户读到的顺序就是源码顺序。

另一条硬性约束:文字内容(body)不能把imgvideopicture作为直接子元素,否则它会被识别成第二个"图",破坏"恰好两个子元素"的结构契约(这一点在 src/recipes/media/manifest.json 的 children 声明中也有明确说明)。

从基础原语构建:sidebar + frame + stack

Yeti 的配方哲学是"快捷方式,而非新发明"。media的几何效果完全等价于三个你已经熟悉的布局原语拼在一起:

  • sidebar:让第一个子元素保持固定宽度,第二个子元素吃下剩余空间,并在空间不足时堆叠;
  • frame:按固定宽高比裁切图片(object-fit: cover);
  • stack:纵向排布文字块,用gap控制文字之间的节奏。

组合形式如下(即 src/recipes/media/docs.md 中给出的完整示例):

<div class="sidebar"><div class="media"><!-- 图片在源码第一位,但被推到右侧显示 --> <div class="media">.media:not([data-gap]) { --_yeti-gap: var(--yeti-space-md); } .media:not([data-width]) { --_yeti-width: var(--yeti-width-xs); } .media:not([data-align]) { --_yeti-align: stretch; } .media:not([data-ratio]) { --_yeti-aspect: 1 / 1; }

这些私有变量是 manifest 中列出的内部 token(--_yeti-gap--_yeti-width--_yeti-align--_yeti-aspect),可能在 minor 版本之间变化,不建议在项目代码中直接依赖;公开 token 则是--yeti-width-xs(默认图片宽度)、--yeti-space-md(默认间距)、--yeti-space-sm(文字块内部子元素间距)。

核心机制:如何识别"图"

media不需要你额外包裹一层<figure>,它通过选择器自动识别"图":

.media > :is(img, video, picture), .media > :has(> :is(img, video, picture)) { flex-basis: var(--_yeti-width); flex-grow: 1; aspect-ratio: var(--_yeti-aspect); overflow: hidden; }

也就是说,直接子元素只要是img/video/picture三者之一,或者包裹着其中之一的任意元素(利用:has()),就会被当作图(figure);其余子元素自动归入"文字主体"。figure 被裁切到设定比例,object-fit: cover保证图片填满而不变形。

picture 包裹的填充细节

当图片外面既有普通元素、又有picture时(即"包裹的 figure"场景),外层规则设置的100%尺寸对picture内部的img并不自动生效——picture默认是块级元素,其高度若不显式设为 100%,img的百分比高度就没有可解析的参照,最终回退为按内容撑开的 auto 高度。因此 media.css 第 30-34 行专门追加了一组规则,把picture本身及其内的img/video/picture全部强制为inline-size: 100%; block-size: 100%,让填充规则穿透两层包裹。test/browser/recipes/media.spec.js 的第四个测试用例(#w-figure#w-figure img矩形几乎完全相等)正是对这一情形的回归验证。

带图注的 figure

如果图片外面包一层带<figcaption><figure>,media 会把裁切范围限定在图片本身,图注完整地排在图片下方:

<div class="media"> <figure> <img src="hills.jpg" alt="A ridgeline at dawn"> <figcaption>A view worth the early start.</figcaption> </figure> <div> <h3>Weekend in the hills</h3> <p>Six miles, one summit.</p> </div> </div>

对应实现见 media.css 第 37-50 行:figure:has(> figcaption)改为纵向 flex、aspect-ratio: autooverflow: visible,仅让图片保持比例,图注使用--yeti-text-sm--yeti-color-text-muted呈现为次要文字。

文字主体:吃下剩余空间并保底一半

主体部分的选择器是"图"选择器的补集:

.media > :not(:is(img, video, picture), :has(> :is(img, video, picture))) { display: flex; flex-direction: column; gap: var(--yeti-space-sm); flex-basis: 0; flex-grow: 999; min-inline-size: 50%; }
  • flex-grow: 999让主体吃掉所有剩余宽度(与 sidebar 的 content 子元素策略一致);
  • min-inline-size: 50%保证文字区域在任何情况下都至少占一半宽度,一旦空间不足,flex-wrap: wrap便触发堆叠;
  • 主体内部按--yeti-space-sm的间隙纵向排列,子元素 margin 被清零(.media > * { margin: 0 }与主体内> * { margin: 0 }双重保证),文字排版完全交给 flex 间隙。

测试与质量保证

test/browser/recipes/media.spec.js 使用 Playwright 驱动真实浏览器,覆盖以下行为(fixture 见 test/browser/fixtures/recipes/media.html):

  • 几何等价:在 1000px 宽视口下,单类形式与组合形式的 figure/body 矩形逐项相等,且 figure 宽度接近--yeti-width-xs、宽高比接近 1;
  • 堆叠行为:在 400px 宽视口下,主体落在 figure 下方(cb.top >= cf.bottom);
  • 源顺序与 contenteditable="false">【免费下载链接】yetiA CSS-first, native, zero-build layout and styling framework for web designers.项目地址: https://gitcode.com/gh_mirrors/fo/yeti

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

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

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

立即咨询