替代 12 列网格的解决方案

2025-06-08

替代 12 列网格的解决方案

这是系列文章的第八篇,该系列文章探讨了过去 13 多年来我作为前端开发者所遇到的各种问题,并提出了相应的解决方案。访问ModernCSS.dev可以查看整个系列文章及其他资源

让我们使用 CSS 网格和 flexbox 创建简化的响应式网格系统,并摆脱重型框架中的大量 12 列网格系统。

如果你还没有真正研究过网格,或者依赖框架来思考弹性框,这将帮助你提升你的理解🚀


纵观整个网络,你经常会看到内容以几种精选风格排列:

  • 其容器的全宽
  • 两个等宽的列
  • 三个等宽的列
  • 四个等宽的列

通常,这是通过大量设置断点宽度的实用程序类来实现的。

在 CSS 网格和 flexbox 之间,并考虑到上述布局,我们可以大大减少响应式网格列的设置。

对于这两种解决方案,我们将只创建两个类,并能够处理 1-4 列内容,这些内容可以响应式地调整大小。

注意:这些解决方案最适合定义主要页面布局容器,但最后我们会提出一些建议来填补其他布局对齐需求的空白。

网格解决方案

正如其名称所示,Grid 擅长网格。这里的“列”和“行”这两个术语是 CSS 网格的固有用法,它们可以使您的解决方案更加清晰。

特别是以下有用的功能:

  • grid-gap- 定义网格项之间的相等空间,无论是列还是行
  • repeat()- 快速定义每行或每列,或一定数量的行或列的规则
  • frunit - 剩余可分配给该列或行的空间“分数”
  • minmax()- 定义可接受的最小和最大列宽或行高

.grid-wrap

首先,我们创建一个包装类。它的作用仅仅是将我们设置的值等效grid-gap于 padding,并且完全是可选的。您可能需要这样做,因为该grid-gap属性不会将间隙应用于网格外部。也许 padding 已经应用于您的包含元素(可能是 )body,或者您可能实际上希望网格列与视口边缘接触。

$gridGap: 2rem;

.grid-wrap {
  padding: $gridGap;
}
Enter fullscreen mode Exit fullscreen mode

.grid

就是这个 - 一个可以快速将任何元素转换为网格容器的类,其中它的直接子元素变为等宽、响应的列。

这是完整的规则,然后我们将对其进行分解:

$minColWidth: 15rem;

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax($minColWidth, 1fr));
  grid-gap: 2rem;

  & + .grid {
    margin-top: $gridGap;
  }
}
Enter fullscreen mode Exit fullscreen mode

首先,我们定义内容列的最小宽度。我建议使用rem此值,以确保整个体验保持一致。如果我们以此为基础设置,则em基础元素字体大小的任何变化都会影响宽度。了解更多关于使用单位的信息 >

那么,魔力就来自于我们如何定义grid-template-columns

我们使用该repeat函数来表示我们希望在所有存在的列中应用相同的参数。

然后,我们不使用绝对数字,而是使用auto-fit负责确保列保持等宽的值,通过拉伸列来填充任何可用空间。

之后,我们使用它minmax()来设置允许的最小列宽,然后使用它1fr作为最大值,以确保内容尽可能地填充列。

.grid然后我们添加间隙,以及在连续容器之间应用相同值的可选规则。

总体来说,解决方案如下:

注意:从技术上讲,您可以在中添加超过 4 列.grid,即使在较大的视口上,它们也会变得更窄,直到达到最小宽度。

观看我的egghead 视频课程,了解如何实现这一技术。

缺点

对于 3 列 + 网格的情况,虽然它确实响应良好,但最终会在某些视口宽度上出现“孤立”列。

您可以使用媒体查询来克服这个问题,但它们会很脆弱。

如果设计中防止孤立列至关重要,那么您可能需要选择 flexbox 解决方案。

Flexbox 解决方案

我们的 flexbox 解决方案将模仿网格,其中优先级是等宽列。

但是,目前还没有完全支持的 flexbox gap 属性(即将推出),所以我们必须采取一些技巧来实现相同的效果。

.flex-grid-wrap

与网格解决方案的意图相同:

$gridGap: 2rem;

.flex-grid-wrap {
  padding: $gridGap;
}
Enter fullscreen mode Exit fullscreen mode

.flex-grid

固有的弹性盒行为将项目放在一行中,其中每个项目随着内容长度而增长,并且随着它的增长它会碰到下一个项目。

因此,我们必须添加一些额外的逻辑来创建等宽行为。

我们用 定义规则display: flex,然后添加一条规则,指导直接子级使用flex评估为以下的行为:

  • flex-grow: 0- 防止超出公平共享的空间范围
  • flex-shrink: 1- 指示元素以相同的速率“收缩”
  • flex-basis: 100%- 抵消了flex-grow仍然扩展项目以填充可用空间的指令
.flex-grid {
  display: flex;

  & > * {
    flex: 0 1 100%;

    &:not(:first-child) {
      margin-left: $gridGap;
    }
  }

}
Enter fullscreen mode Exit fullscreen mode

为了弥补无间隙规则,我们margin-left对除第一项之外的所有项都进行了定义。

小视口的手柄

很好的开始,但对于小视口来说这永远不会失败:

当前弹性列在小视口上的行为

正如一开始所指出的,由于此网格解决方案旨在用于主要页面布局容器,我们将引入媒体查询来插入断点,方法是允许flex-wrap: wrap并将边距“间隙黑客”切换为顶部而不是左边距。

为了确定何时添加换行,基准解决方案将我们可接受的最小宽度乘以 3。这里的逻辑是,一旦 3 列的宽度小于我们可接受的最小宽度,我们就将其拆分成全宽。您可以根据自己可接受的最小宽度调整此规则。

.flex-grid {
  // ...existing styles
  @media (max-width: ($minColWidth * 3)) {
    flex-wrap: wrap;

    & > * {
      margin: 2rem 0 0 !important;
    }
  }

  @media (min-width: ($minColWidth * 3)) {
    & + .flex-grid {
      margin-top: $gridGap;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

我们还添加了一个min-width查询,以便在较大的视口上显示顶部边距“间隙”。如果我们在较小的视口上也设置该查询,则内容组之间的边距最终会加倍,这可能是我们想要的结果。

以下是 flexbox 解决方案演示:

缺点

将此网格应用于页面内的子容器可能会导致不良的断点问题,因为它是一个手动媒体查询,它查看的是视口宽度而不是容器宽度。

可能的补救措施:与其总是应用max-width查询,不如将其应用于类。这样可以将这个基础网格思想应用于子容器,并减少不良结果。

哪一个更好?

所提出的解决方案非常通用但应用范围广泛。

每个目的都是应用于直接子项body,或一层深度,例如main限制整体max-width内容传播但仍与视口同步向下响应的组件。

如果出现以下情况,请选择网格:

  • 您希望利用auto-fit+minmax行为,在达到可接受的最小宽度后自动将项目移动到新行
  • 您计划在子容器中使用,因为媒体查询不需要应用断点(您可以通过设置较小的最小宽度来扩展这个想法以应用于导航栏或卡片操作项等组件)
  • 您希望几乎实现容器查询,因为项目根据其内容长度进行响应

如果符合以下情况,请选择 Flexbox:

  • 唯一需要“网格”行为的地方是布局主要页面容器,例如定义卡片行或创建两列文本内容
  • 你想防止出现“孤立”列

如果你真的想要一个 12 列网格

它就在这里 - 但你有责任按照你喜欢的方式在上面放置项目,这意味着更多的自定义 CSS 规则 :)

.grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  grid-gap: 2rem;
}
Enter fullscreen mode Exit fullscreen mode

或者,您可以创建少量有针对性的类,以便更清晰地定义列的期望值。请注意,这种用法意味着列将精确占用相当于 1/2、1/3 或 1/4 的空间比例。因此,即使网格中只有一列2cols,它仍然只会占据总宽度的一半,而不会填满可用空间。

.grid {
  display: grid;
  grid-gap: 2rem;

  &--2cols {
    grid-template-columns: repeat(2, 1fr);
  }

  &--3cols {
    grid-template-columns: repeat(3, 1fr);
  }

  &--4cols {
    grid-template-columns: repeat(4, 1fr);
  }
}
Enter fullscreen mode Exit fullscreen mode

如果您对包含最小、通用应用程序布局容器和实用程序的基本 HTML/Sass 解决方案的轻量级起点感兴趣,请查看我的快速入门 >

鏂囩珷鏉ユ簮锛�https://dev.to/5t3ph/solutions-to-replace-the-12-column-grid-40jb
PREV
使用 `currentColor` 来保持 CSS DRY 和组件颜色的灵活性
NEXT
可重复使用的 CSS“贴纸”效果