首页
/ Bulma Tiles:用纯 CSS Flexbox 构建 Metro-UI 风格网格

Bulma Tiles:用纯 CSS Flexbox 构建 Metro-UI 风格网格

2026-09-05 13:56:35作者:宣聪麟

本文基于 Bulma 官方博客《Metro UI CSS grid with Bulma tiles》原始文档 展开:当年 Bulma 借 Flexbox 与单一的 tile 元素实现了经典的 Metro-UI 式磁贴网格布局。文章将完整还原 tile is-ancestor / is-parent / is-child 三层类名架构,结合仓库中保留的演示页与端到端测试,给出每一层元素的精确样式契约(负外边距、flex: 1 1 0、12 步尺寸系统等),并说明该特性在 Bulma v1 中的最终归宿,帮助你在旧代码中读懂 Tiles,在新代码中正确迁移。

一个 HTML 元素实现 Metro UI 网格

Bulma 官方博客原文的核心主张非常简洁:

你是否想过用纯 CSS 构建一个 Metro-UI 风格的网格?得益于 Flexbox 与 tile 元素,你现在可以做到了——而且只需要 1 个 HTML 元素:tile 元素。

这里的技术前提有两点:

  • Metro UI 网格的视觉特征:多个内容块(磁贴)无缝拼接,块与块之间只有均匀的小间隙,可以横向平铺、纵向堆叠、任意嵌套,并且同一行里的磁贴默认等宽伸缩;
  • 实现手段:不引入任何 JS,完全靠 display: flex 容器 + flex-grow 均分 + 负外边距/内边距技巧制造间隙。

CHANGELOG 的记录看,.tile 是在 v0.0.19 版本中首次引入的(日志以 “NEW!!! - .tile” 标记),时间上正对应这篇 2016 年 4 月的博客,也就是说 Tiles 是 Bulma 最早期的招牌特性之一。

Tiles 的三层类名架构

Bulma 的 Tiles 并非一个类名打天下,而是把“容器—包裹层—内容块”拆成了三个修饰符,全部挂在同一个 .tile 类上:

类名 角色 职责
tile is-ancestor 祖容器 最外层 flex 容器,用负外边距把内边距技巧“外翻”,消除网格四周的溢出间隙
tile is-parent 父包裹层 提供 12px 内边距,形成磁贴之间的均匀缝隙
tile is-child 内容块 真正可见的磁贴(常叠加 boxnotification is-info 等既有元素),自身外边距归零

仓库中保留的完整演示页 tiles 演示 展示了标准用法,其中嵌套了一个纵向堆叠的子网格:

<div id="tile-ancestor" class="tile is-ancestor">
  <div class="tile is-vertical is-8">
    <div class="tile">
      <div class="tile is-parent is-vertical">
        <article class="tile is-child notification is-warning">
          <p class="title">Vertical...</p>
          <p class="subtitle">Top tile</p>
        </article>

        <article class="tile is-child notification is-warning">
          <p class="title">...tiles</p>
          <p class="subtitle">Bottom tile</p>
        </article>
      </div>

      <div class="tile is-parent">
        <article class="tile is-child notification is-info">
          <p class="title">Middle tile</p>
          <p class="subtitle">With an image</p>
          <figure class="image is-4by3">
            <img src="...">
          </figure>
        </article>
      </div>
    </div>
    <div class="tile is-parent">
      <article class="tile is-child notification is-danger">
        <p class="title">Wide tile</p>
        <p class="subtitle">Aligned with the right tile</p>
      </article>
    </div>
  </div>

  <div class="tile is-parent">
    <article class="tile is-child notification is-success">
      <p class="title">Tall tile</p>
      <p class="subtitle">With even more content</p>
    </article>
  </div>
</div>

这段结构值得逐层读:

  1. is-ancestor 是最外层行容器,直接子元素是两个“大块”:一个 is-vertical is-8(占 12 列中的 8 列、内部纵向排布),一个 is-parent(占剩余 4 列);
  2. is-vertical 把 flex 方向从 row 翻转为 column,于是 is-vertical 内部的两个 is-parent 自上而下堆叠,形成典型的 Metro 式“左列两块 + 右侧一整块 + 左下再一块”的拼贴;
  3. is-child 直接复用其他元素类——例子里叠了 notification is-warning / is-info / is-danger / is-successbox,这说明 Tiles 只是布局层,视觉皮肤完全由既有元素提供,这正是“一个元素解决布局”的设计意图;
  4. 磁贴内部还能再嵌 contentimage is-4by3 等标准组件,层级上不封闭。

从端到端测试还原 Tiles 的精确样式契约

当前仓库中 Tiles 的 CSS 源码虽已随版本演进而移除(在 sass 目录中搜索不到 tile 规则),但仓库保留了完整的 Cypress 端到端测试 tiles.spec.js 与演示页 tiles.html,其中对计算样式(computed style)的断言,恰好是一份精确的样式契约文档。以下数据均可在该测试中逐条验证:

基础 .tile 是均分伸缩的 flex 容器

// docs/cypress/e2e/grid/tiles.spec.js(断言摘要)
expect(cs.alignItems).to.equal("stretch");
expect(cs.display).to.equal("flex");
expect(cs.flexBasis).to.equal("0px");
expect(cs.flexGrow).to.equal("1");
expect(cs.flexShrink).to.equal("1");
expect(cs.minHeight).to.equal("min-content");
  • display: flex + flex: 1 1 0:所有同级磁贴默认等宽伸缩、平分剩余空间,这正是 Metro 网格“同行等宽”观感的来源;
  • align-items: stretch:子块沿交叉轴自动拉伸到等高,保证一行内的磁贴高度对齐;
  • min-height: min-content:防止内容被压缩到不可读。

间隙 = 负外边距 + 内边距的经典组合

is-ancestoris-parent 的断言揭示了 Bulma 制造缝隙的技巧:

// is-ancestor
expect(cs.marginTop).to.equal("-12px");
expect(cs.marginRight).to.equal("-12px");
expect(cs.marginBottom).to.equal("12px");
expect(cs.marginLeft).to.equal("-12px");

// is-parent
expect(cs.padding).to.equal("12px");

// is-child
expect(cs.marginTop).to.equal("0px");
// ...四个方向的外边距全部为 0

机制可以推断为:每个 is-parent 向内垫 12px 内边距,相邻磁贴自然产生 2 × 12px = 24px 的间隙;而 is-ancestor 用四周 -12px 负外边距把这部分“多出来的”空间抵消掉,避免网格整体外溢。其中 margin-bottom 特例是 12px(而非 -12px),让网格底部与页面后续内容保持一个间隙;当祖先容器位于页面最底部时,测试中的 #tile-ancestor-last 断言其 margin-bottom 变为 -12px,把底部间隙也抹平——这是一个容易忽略的边界细节。

上述 12px 对应 Sass 变量 $tile-spacingCHANGELOG 在“Grid”变量清单中明确列出了 $tile-spacing,即磁贴间隙是可定制的单一变量,改动一处即全局生效。

12 步尺寸系统:is-1is-12

演示页末尾有一行 12 个磁贴的“标尺”:

<div class="tile is-ancestor" style="width: 1000px;">
  {% for i in (1..12) %}
    <div id="tile-{{ i }}" class="tile is-{{ i }}">Tile {{ i }}</div>
  {% endfor %}
</div>

测试对其逐一定量校验:

expect(cs.flexGrow).to.equal("0");
expect(cs.flexShrink).to.equal("0");
expect(`${Math.round(actualWidth)}px`).to.equal(
  `${Math.round((i / 12) * 1000)}px`
);

即:在 1000px 宽的祖先容器内,is-N 磁贴的实测宽度严格等于 N/12 × 1000px,且 flex-grow / flex-shrink 均为 0——尺寸类把磁贴从“均分模式”锁定为“1/12 步进固定模式”。这与 Columns 的 12 列体系同构,学习成本几乎为零:想要 8/12 宽就写 is-8

is-vertical 纵向排布

测试同时验证了纵向磁贴的两个要点:

// is-vertical 容器
expect(cs.flexDirection).to.equal("column");

// 纵向排列的 child 磁贴之间
expect(cs.marginBottom).to.equal("24px");

flex-direction: column 让同级磁贴自上而下堆叠;而纵向子块之间以 24px(即两个 $tile-spacing)作为间距,与横向 is-parent 内边距合成的 24px 缝隙保持同一节奏,横纵两个方向上间隙观感一致。

特性演变:从招牌特性到 v1 弃用

理解 Tiles 的现状,需要看它的完整生命周期(均出自 CHANGELOG 与迁移指南):

  1. v0.0.19 引入:日志以 “NEW!!! - .tile” 标记,是 Bulma 早期网格方案的核心(对应本文开篇的博客);
  2. 后续版本补充变量:Grid 变量清单中加入 $tile-spacing,间隙全面变量化;同一时期的日志中还出现 “test tiles” 的移除记录,说明该模块在维护期经历了反复修剪;
  3. v1 弃用:迁移指南 migrating-to-v1.html 明确写道 “Tiles are deprecated”,并给出 Before/After 对照——Before 正是 tile is-ancestor / is-parent / is-child 结构,After 则改用 v1 全新的 Grid 系统(源码位于 sass/grid 目录,含 grid.scsscolumns.scss)。

因此在今天阅读 Bulma v1 源码时,sass/ 目录下已经找不到 tile 规则——仓库中 Tiles 的完整资料,就集中在这篇博客、演示页 tiles.html 与测试 tiles.spec.js 里。如果你在维护仍依赖 Tiles 的旧版页面,建议按迁移指南把磁贴网格改写为新的 Grid;如果只是想学习“纯 CSS 做 Metro 拼贴”的思路,上文基于测试断言还原的样式契约(flex 均分 + 负外边距间隙 + 12 步尺寸)仍然是一份完整、可复现的实现范本。

小结

  • Tiles 用 tile 一个类加 is-ancestor / is-parent / is-child 三个修饰符,构建出容器、缝隙、内容块三层职责清晰的 Metro-UI 式网格,全程零 JS;
  • 样式契约有测试背书:基础磁贴 flex: 1 1 0align-items: stretch;is-parent 内边距与 is-ancestor 负外边距各为 12px 合成 24px 均匀间隙;is-1~is-12 提供 1/12 步进的固定宽度;is-vertical 切换为 column 方向并保持同样的间距节奏;
  • 该特性诞生于 v0.0.19,在 Bulma v1 中被标记弃用并由新 Grid 系统取代;历史资料可追溯至博客原文、CHANGELOG 与保留的 Cypress 测试。
登录后查看全文
热门项目推荐
相关项目推荐