CSS网格:键入响应式20行杂志布局


最近,我致力于博客博客(外部有用/有趣的博客列表)的现代化实现。这样做的目的是为读者提供这些博客的最新帖子的精选内容,以杂志的形式打包,而不是侧边栏中的链接列表。

该任务最简单的部分是从我们最喜欢的RSS提要中获取帖子及其摘录(摘录-kat之前的介绍性文字)的列表。为此,我们使用了Feedzy lite WordPress插件,该插件可以将多个供稿汇总到一个列表中,并按时间排序-这是我们的理想解决方案。困难的部分是使一切变得美丽。

该插件的标准列表界面可能没什么味道,因此我想将其作为报纸或杂志网站的样式,混合使用大小不同的“选定”区域。

听起来是使用CSS Grid的绝佳时机!为不同的布局(例如,一个五列和一个三列)创建一个网格布局,然后使用不同屏幕尺寸的媒体查询在它们之间切换。对?但是,如果您仅可以使用Grid参数auto-fit那么我们是否真的需要这些媒体查询,以及定义控制点的所有麻烦,这将为我们提供自适应网格?

这个想法似乎吸引我,但是当我开始加入跨电网的若干列元素(跨度),网格开始在狭窄的屏幕上爬出页面。媒体查询似乎是唯一的解决方案。但是我发现更好的东西!

研究了CSS Grid上的许多文章后,我发现它们主要分为两种类型:

  1. 如何创建具有跨度但具有给定列数的有趣布局。
  2. 如何在网格上创建具有相同宽度的列(即无跨度)自适应布局。

我希望网格既可以实现此功能又可以实现:使用自适应调整多列元素大小的完全自适应布局。

这样做的好处是,一旦您开始了解自适应网格的局限性以及跨度为何以及何时打破适应性,就可以轻松创建带有数十行代码和一个媒体查询的杂志版式(或者如果您想限制跨度的多样性,甚至不用它们)。

这是包装盒中RSS插件与我们的工作结果(可点击)的清晰对比:


这是一种完全自适应的杂志版面,带有彩色的“选定”框,可根据列数动态地适应该版面。该页面显示大约50个帖子,但是布局代码不取决于元素的数量。您可以在插件设置中轻松地将帖子数增加到100,而布局在最底层仍然很有趣。

所有这些都是通过CSS唯一实现的,并且仅使用一个媒体查询就可以在最窄的屏幕(小于460像素)的一列中显示内容。

最不可思议的是,整个布局仅用了21行CSS(不包括网站的常规样式)。但是,为了使用很少的代码来实现这种灵活性,我不得不深入研究CSS网格的最深层次,并学习如何克服其某些局限性。

整个布局所依赖的代码非常短,这要归功于CSS Grid的出色表现:

.archive {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  grid-gap: 32px;
  grid-auto-flow: dense;
}

/*    */
.article:nth-child(31n + 1) {
  grid-column: 1 / -1;
}
.article:nth-child(16n + 2) {
  grid-column: -3 / -1;
}
.article:nth-child(16n + 10) {
  grid-column: 1 / -2;
}

/*     */
@media (max-width: 459px) {
  .archive {
    display: flex;
    flex-direction: column;
  }
}

本文中描述的技术可以安全地用于对任何动态生成的内容进行样式化,无论它是最新帖子,存档页面或搜索结果的小部件的输出。

创建自适应网格


我创建了17个元素来展示未来内容的多样性-标题,图片和摘录,并包装在 <div></div>

<div class="archive">
  <article class="article">
    <!--  -->
  </article>

  <!--  16  -->

</div>

将这些元素转换为自适应网格的代码特别紧凑:

.archive {
  /*     - */
  display: grid;
  /*        ,       180 . */
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  /*     */
  grid-gap: 1em;
}

→CodePen上的演示

请注意,行高如何自动调整为行中最高的内容块。如果从上面的链接更改演示中的宽度,您将看到元素自动增加和减少,列数分别从1更改为5。

在这里,我们看到了名为的CSS Grid魔术auto-fit此关键字与minmax()应用于的功能结合使用grid-template-columns

怎么运行的


五列布局本身可以这样获得:

.archive {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
}

但是,这将创建五列布局,该布局在不同的屏幕宽度下会拉伸和收缩,但始终保持五列,这会导致小屏幕上的列非常狭窄。首先想到的是编写一堆媒体查询并重新定义具有不同列数的网格。那会起作用,但是关键字auto-fit会自动完成所有操作。

auto-fit根据需要使用该功能minmax()。因此,我们有点告诉浏览器您可以压缩多少列,以及可以压缩多少列。当达到边界之一时,列数分别增加或减少。

.archive {
  grid-template-columns: repeat (auto-fit, minmax(180px, 1fr));
}

在此示例中,浏览器将尝试容纳多达180像素宽的列。如果有多余的空间,则所有列都会扩展,将它们平均分配。这就是决定含义的原因1fr:使列大小等于可用宽度的分数(fr个动作)。

如果拉伸浏览器窗口,则所有列将随着可用空间的增加而同等增长。新发现的空间一旦达到180像素,就会在其位置出现一个新列。而且,如果您缩小浏览器窗口,则所有操作都将以相反的方式进行,从而完美地拟合网格,直到它变成单列布局为止。魔法!

→视频演示

有了这行代码,所有这些适应性便得到了体现。好酷吗?

使用“自动流:密集”创建跨度


因此,目前我们已经有了一个自适应网格,这就是它的所有元素-相同的宽度。报纸或杂志的布局意味着存在选定的块,在这种情况下,这些块将覆盖两个,三个甚至所有可用的列。

要创建多列跨度,我们可以grid-column: span在应该占用更多空间的元素中使用属性假设我们希望第三个列表项的宽度为两列:

.article:nth-child(3) {
  grid-column: span 2;
}

但是,添加跨度后,可能会出现很多问题。首先,在这种情况下,当宽元素不适合其线并将其auto-fit转移到以下位置时,可能会在网格中形成孔


通过向grid-auto-flow: dense网格添加属性可以轻松解决此问题由于具有此属性,浏览器知道需要用其他元素填充漏洞。这围绕较窄的较宽元素创建了一个流程:


请注意:元素的顺序已损坏,现在第四个元素位于第三个元素的前面。据我所知,这不能被绕过,这是必须接受的CSS Grid的限制之一。

识别跨度的方法


有几种方法可以指定项目应占用的列数。最容易应用于grid-columns: span [n]元素之一,其中n元素将占据的列数为。布局中的第三个元素的属性为register grid-column: span 2,这说明了为什么其宽度是其他元素的两倍。

要使用其他方法,必须指定网格线网格线编号如下:


可以使用正数(例如1、2、3)从左至右指示网格线,或使用负数(-1,-2,-3)从右至左指示网格线。它们可以用于使用属性将元素放置在网格中grid-column,如下所示:

.grid-item {
  grid-column: ( ) / ( );
}

因此,网格线扩展了我们定义跨度的能力。通过使用关键字替换初始值或最终值的能力来增加灵活性span例如,可以通过将以下任何属性应用于第八个网格元素来创建以上示例中的三列蓝色块:

  • grid-column: 3 / 6
  • grid-column: -4 / -1
  • grid-column: 3 / span 3
  • grid-column: -4 / span 3
  • grid-column: span 3 / -1
  • 等等

在非自适应网格(即,具有固定的列数)中,这些属性中的每一个都给出相同的结果(与上面带有蓝色框的示例一样)。但是,如果网格是自适应的并且列数发生了变化,则差异将变得非常明显。某些跨度会在自动包装打开的情况下破坏布局,这似乎使这两种解决方案不兼容。幸运的是,一些技巧将使我们能够安全地将它们组合在一起。

但是首先,我们需要了解问题。

水平滚动问题


以下是使用网格线方法(可单击)创建的一些“特色项目”:


在整个宽度(五列)上,一切看起来都不错,但是如果将屏幕缩小到应该有两列的大小,则布局会中断:


如您所见,我们的网格已经失去了适应性,尽管容器已经缩小,但是网格正在尝试支持所有五列。为此,她继续尝试使用相同的列宽,并最终超出了右侧容器的边界。由此,出现水平滚动。

为什么会发生?问题在于浏览器正在尝试遵守我们关于网格线的确切指示。在此宽度下,auto-fit网格仅应显示两列,但是我们的堆栈行编号系统与此相反,特别是指向第五行。这种矛盾导致混乱。为了正确显示我们隐含的两列网格,只能使用数字1、2、3和-3,-2,-1,如下所示:


但是,如果网格的元素之一包含grid-column超出这些限制的指令(例如4、5或-6),则浏览器会收到不明确的指令。一方面,我们要求您自动创建灵活的列(在此屏幕宽度下,应隐式地保留两个)。另一方面,我们明确提到了网格线,网格线不能以两列格式存在。当隐式(自动)列与其显式定义的编号之间存在矛盾时,网格始终倾向于使用显式定义。这是不需要的列和水平溢出出现的方式(它们称为CSS数据丢失跨度(如网格线号)可以创建显式的列定义。grid-column:尽管我们想要两个隐式列,但跨度3(演示中的第八个网格元素)强制网格显式包含至少三列。

似乎唯一的选择是使用媒体查询将值更改grid-column为所需的宽度...但不要着急!一开始我也这么认为。但是,经过一番思考并使用了不同的设置,我发现有一些方法可以解决此问题,由于这种原因,对于最狭窄的设备,我们仍然只有一种媒体要求。

解决方案


事实证明,诀窍是仅使用可用于计划显示的最窄网格的行号来确定跨度。在这种情况下,我们谈论的是两列网格(回想一下,我们使用媒体查询来显示单列)。因此,您可以安全地使用数字1、2、3及其负对,而不会破坏网格。

最初,我认为我将使用这些数字组合将自己限制为两列的跨度宽度:

  • grid column: span 2
  • grid-column: 1 /3
  • grid-column: -3 / -1


最多可完美适应两个扬声器:


尽管这是可行的,但从设计的角度来看,这是一个严重的限制,并且不太明显。我想在宽屏幕上跨度为三,四甚至五列。但是如何?我最初的想法是再次返回媒体查询(糟糕,很难摆脱旧习惯),但我仍然尝试避免使用这种方法,而是从另一个角度来看响应式设计。

再次查看可用数字列表时,我突然意识到可以将初始值和最终值中的正数和负数grid-column进行组合,例如1/-32/-2。似乎没什么意思。但是,当您在更改网格大小后了解了线条的位置时,似乎就不再像以前那样:跨距会根据屏幕的宽度来更改宽度。大量的自适应跨度机会开辟了新局面,尤其是那些跨越不同列数且屏幕宽度发生变化而无需任何媒体查询的元素。

我发现的第一个示例是grid-column: 1/-1。此属性将元素变成全角的横幅,从头到尾填充所有列,即使只有一列也是如此!

使用grid-column: 1/-2,您可以创建一个跨度“几乎全宽”,该跨度从左到右填充所有列,除了最后一个。在两列布局中,这样的跨度会自适应地变成一列中的普通元素。令人惊讶的是,即使将布局压缩到一列,它也可以工作。 (似乎原因是网格不会将元素减小到零宽度,因此它保持了一列的宽度,就像使用的情况一样grid-column: 1/1。)我假设它grid-column: 2/-1应该以相同的方式工作,只保留一列在左侧而不是在右侧。结果几乎是正确的,当将布局压缩到一列时,仍然会发生溢出。

然后我尝试了组合1/-3。它在宽屏幕上效果很好-至少填充三列-在窄屏幕上-仅填充一列。我认为对于两列网格,结果会有些奇怪,因为网格的第一行与数字下的行相同-3。令我惊讶的是,该项目正确显示在一列中。

经过大量的实验,我发现grid-column在两列网格中可以找到11个合适的值。其中三个甚至可以在单列布局中工作。其他七个可以正常工作到两列,并且为了在一列中正确显示,他们只需要一个媒体查询。

以下是完整列表:


演示自动调整网格中不同屏幕尺寸上的自适应网格列值。演示

通常,尽管自适应跨度的子集非常有限,但仍有很多机会。

  • 2/-2 -一个有趣的组合,创建了一个中心跨度,该跨度一直到单列网格!
  • 3/-1 -最没有用,因为即使在两列上也会导致溢出。
  • 3/-3 -一个惊喜。

由于grid-column此列表中的值多种多样,因此可以创建有趣且完全响应的布局。通过对最窄的单列显示使用单个媒体查询,我们可以管理十种不同的模式grid-column

相同的媒体查询非常简单,甚至可以说很简单。在我们的示例中,他负责将网格显示切换到flexbox:

@media (max-width: 680px) {
  .archive {
    display: flex;
    flex-direction: column;
  }

  .article {
    margin-bottom: 2em;
  }
}

这是最后一个网格,您可能已经注意到,它完全响应-从一到五列(可单击):


用法:nth-​​child()用于重复动态宽度


为了将代码减少到两行,我应用了另一个技巧。选择:nth-child(n)器使我可以一次设置大量元素的样式。我对跨度的整个想法是应用于提要中的许多帖子,以便选定的块会定期出现在页面上。首先,我编写了一个用逗号分隔的选择器列表,其中包含明确定义的元素编号:

.article:nth-child(2),
.article:nth-child(18),
.article:nth-child(34),
.article:nth-child(50)  {
  background-color: rgba(128,0,64,0.8);
  grid-column: -3 / -1;
}

在速度上,我意识到这是一个非常耗时的过程,尤其是当您必须为文章中需要更改的每个子元素复制整个条件列表时,例如标题,链接等。在进行原型制作时,每次我要调整跨度的位置时,我都必须手动更改每个列表中的数字。一个无聊且容易出错的过程。

那时我意识到您可以利用伪选择器的出色功能:nth-child:例如:nth-child(2n+ 2)输入一个表达式(而不是整数值),这意味着每个第二个子元素。

这是我过去:nth-child([])在网格中创建全角蓝色块的方式,该块出现在页面顶部,然后在列表的一半处:

.article:nth-child(31n + 1) {
  grid-column: 1 / -1;
  background: rgba(11, 111, 222, 0.5);
}

方括号(31n + 1)中的一段代码负责选择第1,第32,第63等。子元素。浏览器从n = 0(31 * 0 + 1 = 1开始循环,然后是n=131 * 1 + 1 = 32),最后是n=231 * 2 + 1 = 63)。在后一种情况下,浏览器了解没有第63个子元素,忽略该规则,停止循环并将该规则应用于第1个和第32个元素。

我正在为整个页面左侧或右侧显示的紫色块执行类似的操作:

.article:nth-child(16n + 2) {
  grid-column: -3 / -1;
  background: rgba(128, 0, 64, 0.8);
}

.article:nth-child(16n + 10) {
  grid-column: 1 / -2;
  background: rgba(128, 0, 64, 0.8);
}

第一个选择器用于左边的紫色块。该表达式16n + 2负责将样式从第一个栅格元素应用于每个第16个栅格元素。

第二个选择器用于右边的紫色块。间隔相同(16n),但偏移量不同(10)。结果,这些块通常在网格的右侧,编号为10、26、42等的元素中找到。

对于这些元素的视觉样式,我使用了另一个技巧来避免代码重复。对于常见的紫色块样式(background-color例如,显而易见的),可以使用一个选择器:

.article:nth-child(8n + 2) {
  background: rgba(128, 0, 64, 0.8);
  /* Other shared syling */
}

此选择器将选择项目2、10、18、26、34、42、50等。换句话说,他选择了左块和右块。

之所以起作用,是因为8n-这恰好是一半16n,并且两个选择器中的移位差也为8。

最后的话


CSS Grid现在可用于以最少的代码创建灵活的响应式网格。但是,如果避免使用逆向媒体查询,则网格中元素的位置将受到很大限制。

能够创建不会导致水平滚动和在小屏幕上溢出的跨度将非常酷。现在我们可以告诉浏览器:“请创建一个自适应网格,”它做得很好。但是,您只需要添加:“哦,请把网格的这个元素拉伸成四列,”然后他将手柄摇到狭窄的屏幕上,从而优先选择四列跨度的请求,而不是自适应网格。可以使网格做相反的事情,例如像这样:

.article {
  grid-column: span 3, autofit;
}

响应网格的另一个问题是最后一行。更改屏幕宽度通常会使其变为空白。我花了很长时间尝试找到一种方法,以将网格的最后一个元素拉伸到其余的列(分别填充行),但这似乎是不可能的。至少现在(是。最好有机会像这样用关键字设置元素的初始位置auto,说“从左边缘开始将线填充到末尾”。像这样:

.article {
  grid-column: auto, -1;
}

...这会将网格左边缘的跨度拉伸到行尾。


All Articles