如何使用卡片制作响应式 CSS 布局,使卡片的顶部和底部部分与行中最高的部分保持相同的高度

问题描述 投票:0回答:1

我有一个布局,我想在其中显示多个“卡片”。每张卡片由垂直排列的 3 个部分组成。顶部部分将包含针对不同卡片长度不同的文本,并且最终可能足够长以换行至 3 行。中间部分将包含一个 div,其中可以包含各种其他元素,每张卡片的高度都不同。底部要么是空的,要么只有一行文本。如果一行中没有一张卡片的底部有文本,我不希望它们占用任何空间。我希望卡片具有响应能力,这样如果它们不能并排放置,它就会包裹起来并将它们放入网格图案中,如果屏幕足够小,卡片就会全部显示在单列中,从上到下。我正在努力解决的问题是,我希望同一行上的所有卡片都与该行中最高的卡片具有相同的高度,并且我希望每张卡片的每个部分的顶部和底部都在同一行上要对齐的行。

这基本上就是我希望它在更大屏幕上显示的样子: A layout with 3 cards fitting in the given width

在较小的屏幕上:

A layout with 2 cards fitting in the given width

我的第一个想法是 CSS 网格会起作用。我可以将每张卡片的顶部放在第一行,中间部分放在第二行,底部放在第三行。这样可以轻松获得相同的高度并保持所有内容对齐。但我不知道如何使其按照我想要的方式响应。使用

auto-fit
将一次包裹一个单元格,这只是卡片的一部分。

.container {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(133px, 1fr));
  gap: 30px 5px;
  align-items: end;
  border: 1px solid grey;
  margin-bottom: 20px;
}

.middle {
  align-self: start;
  background-color: lightblue;
}

.a2 {
  height: 50px;
}

.b2 {
  height: 30px;
}

.c2 {
  height: 15px;
}

.optional {
  font-size: .6rem;
}

.pseudo-screen-large {
  width: 500px;
}

.pseudo-screen-medium {
  width: 300px;
}

.pseudo-screen-small {
  width: 150px;
}
On a large screen everything looks good:
<div class="pseudo-screen-large">
  <div class="container">
    <span>A1 - Short Text</span>
    <span>B1 - Longer Text that is going to wrap to 3 lines because it is really long</span>
    <span>C1 - Medium Text that is going to wrap to 2 lines</span>
    <div class="middle a2">A2</div>
    <div class="middle b2">B2</div>
    <div class="middle c2">C2</div>
    <div class="optional">*Explanatory Text</div>
    <div class="optional"></div>
    <div class="optional">*Explanatory Text</div>
  </div>
</div>

Even with no optional text, we get some extra whitespace because of the row gap. I haven't tried to fix that yet, because of the other problems.
<div class="pseudo-screen-large">
  <div class="container">
    <span>A1 - Short Text</span>
    <span>B1 - Longer Text that is going to wrap to 3 lines because it is really long</span>
    <span>C1 - Medium Text that is going to wrap to 2 lines</span>
    <div class="middle a2">A2</div>
    <div class="middle b2">B2</div>
    <div class="middle c2">C2</div>
    <div class="optional"></div>
    <div class="optional"></div>
    <div class="optional"></div>
  </div>
</div>

But once we start wrapping on smaller screens, there's immediately a problem:
<div class="pseudo-screen-medium">
  <div class="container">
    <span>A1 - Short Text</span>
    <span>B1 - Longer Text that is going to wrap to 3 lines because it is really long</span>
    <span>C1 - Medium Text that is going to wrap to 2 lines</span>
    <div class="middle a2">A2</div>
    <div class="middle b2">B2</div>
    <div class="middle c2">C2</div>
    <div class="optional">*Explanatory Text</div>
    <div class="optional hide">*Explanatory Text</div>
    <div class="optional">*Explanatory Text</div>
  </div>
</div>

<div class="pseudo-screen-small">
  <div class="container">
    <span>A1 - Short Text</span>
    <span>B1 - Longer Text that is going to wrap to 3 lines because it is really long</span>
    <span>C1 - Medium Text that is going to wrap to 2 lines</span>
    <div class="middle a2">A2</div>
    <div class="middle b2">B2</div>
    <div class="middle c2">C2</div>
    <div class="optional">*Explanatory Text</div>
    <div class="optional hide">*Explanatory Text</div>
    <div class="optional">*Explanatory Text</div>
  </div>
</div>

我可以让每张卡片在网格中都有自己的单元格,然后在每个卡片单元格中使用弹性盒或另一个网格来表示卡片的各个部分,但是同一行上的卡片顶部之间没有任何关系,以保持它们对齐。

.container {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(133px, 1fr));
  gap: 30px 5px;
  border: 1px solid grey;
  margin-bottom: 30px;
}

.card {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}

.card-grid {
  display: grid;
  align-items: end;
}

.middle {
  background-color: lightblue;
}

.card-grid .middle {
  align-self: start;
}

.a2 {
  height: 50px;
}

.b2 {
  height: 30px;
}

.c2 {
  height: 15px;
}

.optional {
  font-size: .6rem;
}

.pseudo-screen-large {
  width: 500px;
}

.pseudo-screen-medium {
  width: 300px;
}

.pseudo-screen-small {
  width: 150px;
}
Here we get the wrapping how I want, but the vertical alignment of the cards is all messed up:
<div class="pseudo-screen-medium">
  <div class="container">
    <div class="card">
      <span>A1 - Short Text</span>
      <div class="middle a2">A2</div>
      <div class="optional">*Explanatory Text</div>
    </div>
    <div class="card">
      <span>B1 - Longer Text that is going to wrap to 3 lines because it is really long</span>
      <div class="middle b2">B2</div>
      <div class="optional"></div>
    </div>
    <div class="card">
      <span>C1 - Medium Text that is going to wrap to 2 lines</span>
      <div class="middle c2">C2</div>
      <div class="optional">*Explanatory Text</div>
    </div>
  </div>
</div>

Using a subgrid instead of a flexbox for the cards results in the same:
<div class="pseudo-screen-medium">
  <div class="container">
    <div class="card-grid">
      <span>A1 - Short Text</span>
      <div class="middle a2">A2</div>
      <div class="optional">*Explanatory Text</div>
    </div>
    <div class="card-grid">
      <span>B1 - Longer Text that is going to wrap to 3 lines because it is really long</span>
      <div class="middle b2">B2</div>
      <div class="optional"></div>
    </div>
    <div class="card-grid">
      <span>C1 - Medium Text that is going to wrap to 2 lines</span>
      <div class="middle c2">C2</div>
      <div class="optional">*Explanatory Text</div>
    </div>
  </div>
</div>

由于我想要在卡片级别进行包装,因此 Flexbox 也很有意义。然而,这会导致与网格相同的问题,每张卡都在自己的单元格中。

.container {
  display: flex;
  flex-wrap: wrap;
  gap: 20px 5px;
  margin-bottom: 20px;
}

.card {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  width: 161px;
  border: 1px solid grey;
}

.middle {
  background-color: lightblue;
}

.a2 {
  height: 50px;
}

.b2 {
  height: 30px;
}

.c2 {
  height: 15px;
}

.optional {
  font-size: .6rem;
}

.hide {
  visibility: hidden;
}

.pseudo-screen-large {
  width: 500px;
}

.pseudo-screen-medium {
  width: 350px;
}

.pseudo-screen-small {
  width: 150px;
}
Again, everything wraps well, but the vertical alignment is all messed up:
<div class="pseudo-screen-medium">
  <div class="container">
    <div class="card">
      <span>A1 - Short Text</span>
      <div class="middle a2">A2</div>
      <div class="optional">*Explanatory Text</div>
    </div>
    <div class="card">
      <span>B1 - Longer Text that is going to wrap to 3 lines because it is really long</span>
      <div class="middle b2">B2</div>
      <div class="optional"></div>
    </div>
    <div class="card">
      <span>C1 - Medium Text that is going to wrap to 2 lines</span>
      <div class="middle c2">C2</div>
      <div class="optional">*Explanatory Text</div>
    </div>
  </div>
</div>

我可以将所有中间部分和底部部分的高度设置为每个部分存在的最大高度。但是,当一张卡片位于最大的卡片不在的一行时,就会出现很多多余的空白。

.container {
  display: flex;
  flex-wrap: wrap;
  gap: 30px 5px;
  border: 1px solid grey;
  margin-bottom: 20px;
}

.card {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  width: 161px;
}

.middle-container {
  height: 50px;
}

.middle {
  background-color: lightblue;
}

.a2 {
  height: 50px;
}

.b2 {
  height: 30px;
}

.c2 {
  height: 15px;
}

.optional {
  font-size: .6rem;
  height: 11px;
}

.hide {
  visibility: hidden;
}

.pseudo-screen-large {
  width: 500px;
}

.pseudo-screen-medium {
  width: 350px;
}

.pseudo-screen-small {
  width: 150px;
}
Here everything wraps correctly, and the vertical alignment is pretty good. But we have a huge gap below C2 & D2, and a smaller gap below E2, neither of which I want.
<div class="pseudo-screen-medium">
  <div class="container">
    <div class="card">
      <span>A1 - Short Text</span>
      <div class="middle-container">
        <div class="middle a2">A2</div>
      </div>
      <div class="optional">*Explanatory Text</div>
    </div>
    <div class="card">
      <span>B1 - Longer Text that is going to wrap to 3 lines because it is really long</span>
      <div class="middle-container">
        <div class="middle b2">B2</div>
      </div>
      <div class="optional"></div>
    </div>
    <div class="card">
      <span>C1 - Medium Text that is going to wrap to 2 lines</span>
      <div class="middle-container">
        <div class="middle c2">C2</div>
      </div>
      <div class="optional"></div>
    </div>
    <div class="card">
      <span>D1 - Short Text</span>
      <div class="middle-container">
        <div class="middle c2">D2</div>
      </div>
      <div class="optional">*Explanatory Text</div>
    </div>
    <div class="card">
      <span>E1 - Short Text</span>
      <div class="middle-container">
        <div class="middle a2">E2</div>
      </div>
      <div class="optional"></div>
    </div>
  </div>
</div>

我发现唯一有效的方法是使用上面第一个片段中的网格,而不是依赖

auto-fit
来使其响应,我将使用带有
grid-template-areas
的媒体查询来显式布局每个单元格的位置应放置。但这需要对单张卡宽度的屏幕使用媒体查询,对两张卡宽度的屏幕使用另一个媒体查询,等等。并且需要为每张卡分配自己的
grid-area
名称。这是可能的,并且可能可以使用 Sass mixin 来简化,但它看起来仍然相当笨拙,所以我想知道是否有更简单的方法。

css layout flexbox css-grid
1个回答
0
投票

使用 actual

subgrid
似乎可以解决您的问题,如对齐子元素不同块

中所述

* {
  margin: 0
}

.container {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(133px, 1fr));
  gap: 5px;
  border: 1px solid grey;
  margin-bottom: 30px;
}

.card {
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 3;
  border: 1px solid red;
}

.middle {
  background-color: lightblue;
}

.optional {
  font-size: .6rem;
}

.pseudo-screen-large {
  width: 600px;
}

.pseudo-screen-medium {
  width: 300px;
}

.pseudo-screen-small {
  width: 150px;
}
Small:
<div class="pseudo-screen-small">
  <div class="container">
    <div class="card">
      <span>A1 - Short Text</span>
      <div class="middle a2">A2</div>
      <div class="optional">*Explanatory Text</div>
    </div>
    <div class="card">
      <span>B1 - Longer Text that is going to wrap to 3 lines because it is really long</span>
      <div class="middle b2">B2</div>
      <div class="optional"></div>
    </div>
    <div class="card">
      <span>C1 - Medium Text that is going to wrap to 2 lines</span>
      <div class="middle c2">C2</div>
      <div class="optional">*Explanatory Text</div>
    </div>
  </div>
</div>

Medium:
<div class="pseudo-screen-medium">
  <div class="container">
    <div class="card">
      <span>A1 - Short Text</span>
      <div class="middle a2">A2</div>
      <div class="optional">*Explanatory Text</div>
    </div>
    <div class="card">
      <span>B1 - Longer Text that is going to wrap to 3 lines because it is really long</span>
      <div class="middle b2">B2</div>
      <div class="optional"></div>
    </div>
    <div class="card">
      <span>C1 - Medium Text that is going to wrap to 2 lines</span>
      <div class="middle c2">C2</div>
      <div class="optional">*Explanatory Text</div>
    </div>
  </div>
</div>

Large:
<div class="pseudo-screen-large">
  <div class="container">
    <div class="card">
      <span>A1 - Short Text</span>
      <div class="middle a2">A2</div>
      <div class="optional">*Explanatory Text</div>
    </div>
    <div class="card">
      <span>B1 - Longer Text that is going to wrap to 3 lines because it is really long</span>
      <div class="middle b2">B2</div>
      <div class="optional"></div>
    </div>
    <div class="card">
      <span>C1 - Medium Text that is going to wrap to 2 lines</span>
      <div class="middle c2">C2</div>
      <div class="optional">*Explanatory Text</div>
    </div>
  </div>
</div>

© www.soinside.com 2019 - 2024. All rights reserved.