让我们使用CSS grid和flexbox来创建简化的响应式网格系统,并抛弃大量来自重型框架的12列网格系统。
如果你还没有真正研究过网格,或者依赖框架为你考虑Flexbox,这将有助于你加深理解🚀。
纵观整个网络,你会经常看到内容以几种特定的方式布置:
- 容器的全宽
- 两个等宽的列
- 三个等宽的列
- 四个等宽的列
通常,这是由相当多的实用类设置跨断点的宽度完成的。
在CSS grid和flexbox之间,并考虑到前述的布局,我们可以大大减少响应式网格列的设置。
对于这两种解决方案,我们将只创建两个类,并能够处理从1-4列的内容,响应地平等调整🙌。
注意:这些解决方案对于定义主要的页面布局容器来说效果最好,但我们最后会提出一些建议,以填补其他布局对齐需求的空白。
网格解决方案
Grid擅长网格,正如它的名字所暗示的那样。在这里,"列 "和 "行 "是你使用CSS网格的固有方式,这可以使你的解决方案的定义更加清晰。
特别是以下这些有用的功能:
grid-gap- 定义网格项目之间的相等空间,无论是列还是行repeat()- 快速定义每一行或每一列的规则,或一组行或列的数量fr单位--可用于分配给该列或行的空间的 "分数"minmax()- 定义一个最小和最大的可接受的列宽或行高
.grid-wrap
首先,我们创建一个包装类。这只是为了应用相当于我们的grid-gap 值作为填充,完全是可选的。你可能想要这个,因为grid-gap 属性并没有将间隙间距应用到网格的外部。也许padding已经应用在你的包含元素上了,这个元素可能是body ,或者你可能真的希望你的网格列能够接触到视口的边到边。
$gridGap: 2rem;
.grid-wrap {
padding: $gridGap;
}
.grid
这就是它--一个可以快速将任何元素变成网格容器的类,它的直接子元素就会变成等宽的、响应式的列。
下面是完整的规则,然后我们将把它分解:
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax($minColWidth, 1fr));
gap: 2rem;
& + .grid {
margin-top: $gridGap;
}
}
首先,我们为我们的内容列定义一个最小宽度。我建议这个值使用rem ,这样它在整个体验中是一致的。如果我们根据em ,它将随着基本元素字体大小的改变而改变。了解更多关于使用单位的工作 >
然后,神奇之处在于我们如何定义grid-template-columns 。
我们使用repeat 函数来表示我们希望在所有存在的列中应用相同的参数。
然后,我们使用auto-fit ,而不是一个绝对数字,它负责确保列保持等宽,通过拉伸列来填补任何可用的空间。
之后,我们使用minmax() 来设置允许的最小列宽,然后使用1fr 作为最大列宽,确保内容在空间允许的范围内填满该列。
然后,我们添加我们的间隙,以及一个可选的规则,在连续的.grid 容器之间应用相同的值。
这就是全部的解决方案。
缺点
在3列+网格的情况下,虽然它的反应很好,但在某些视口宽度上,你会出现一个 "孤儿 "列。
你可以用媒体查询来克服这个问题,但它们会很脆弱。
如果防止无主列对设计至关重要,你可能想选择flexbox解决方案。
Flexbox解决方案
我们的 Flexbox 解决方案将模仿网格,优先考虑等宽的列。
然而,目前还没有一个完全支持Flexbox的间隙属性(即将推出!),所以我们必须采取一些技巧来达到同样的效果。
.flex-grid-wrap
与网格解决方案的意图相同:
$gridGap: 2rem;
.flex-grid-wrap {
padding: $gridGap;
}
.flex-grid
固有的flexbox行为将项目放在一排,每个项目随着内容长度的增长而增长,当它增长时,会将下一个项目撞过来。
因此,我们必须添加一些额外的逻辑来创建等宽的行为。
我们用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;
}
}
}
为了弥补无间隙规则,我们在除第一个项目外的所有项目上定义了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;
}
}
}
我们还添加了一个min-width 查询,这样我们就可以在较大的视口上有上边距的 "间隙"。如果我们在小视窗上也这样做,我们就会在各组内容之间产生双倍的空白,这可能是一个理想的结果。
缺点
对页面中的子容器应用这个网格可能会导致不理想的断点问题,因为它是一个手动媒体查询,看的是视口宽度而不是容器宽度。
可能的补救措施:不要总是应用max-width 查询,你可以用一个类来应用它。这样就可以在子容器中使用这个基础网格的想法,并减少不理想的结果。
哪个更好?
所提出的解决方案是非常普遍的,但有广泛的应用。
每个方案的意图都是应用于body 的直接子节点,或者深入一层,比如一个main 组件,该组件限制了内容传播的整体max-width ,但仍然与视口同步向下响应。
选择网格
- 你想利用
auto-fit+minmax的行为,一旦达到最小可接受的宽度,就自动将项目撞到新的一行。 - 你打算在子容器中使用,因为媒体查询不需要应用断点(你可以通过设置较小的最小宽度,将这个想法扩展到导航条或卡片动作项等组件上)。
- 你想几乎实现容器查询,因为项目是根据其内容长度来响应的
选择Flexbox
- 你需要 "网格 "行为的唯一地方是布局主要的页面容器,例如定义卡片的行或创建两栏文本内容
- 你想防止出现 "孤儿 "列
如果你真的想要一个12列的网格,那么你可以使用这个网格
这就是了--但你要负责按照你的意愿在上面放置项目,这意味着要有更多的自定义CSS规则:)
.grid {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: 2rem;
}
另外,你也可以只创建一些有针对性的类来更清楚地定义列的期望。请注意,这种类型的用法意味着列将精确地占用等于1/2、或1/3、或1/4的空间的部分。因此,如果你在2cols 网格中只有一列,它仍然只会横跨总宽度的一半,而不会填满可用空间:
.grid {
display: grid;
gap: 2rem;
&--2cols {
grid-template-columns: repeat(2, 1fr);
}
&--3cols {
grid-template-columns: repeat(3, 1fr);
}
&--4cols {
grid-template-columns: repeat(4, 1fr);
}
}