使用CSS Grid和Flexbox制作Card

“Card”这样的UI组件在现在的Web中经常可见,但我们制作这方面的UI组件方式仍然受到一定的限制。直到现在,通过CSS GridFlexbox的组合,可以使卡整齐对齐,响应性更好,并适应其中的内容。接下来看看我们是如何制作的。

我们将要做的

我们将要做的一个Card UI组件(点击这里可以全屏查看效果,这样你会看得更清楚)。

上面的效果,在各种断点下(缩小浏览器屏幕),Card的布局将会像下面那样进行改变:

Card

CSS Grid vs Flexbox

Flexbox在CSS中出现,可以说让全世界的Web开发者欢呼起来,终于有一个布局模块来解决CSS 浮动布局给我们带来的种种麻烦与拙折。但事实并非如此。Flexbox最适合是沿单个轴分布元素,要么沿水平方向或者沿垂直方向排列元素。也就是说要使用Flexbox来构建真正的流体网格布局还是非常困难的。

然而,CSS Grid旨在跨越两个轴(或维度)排列元素,也就是说水平和垂直方向都可以排列元素。在这篇文章中,通过使用CSS Grid来实现文章开头展示的Card布局效果,这也是这篇文章的真正的目的。让我得到一个真正的解决流体网格布局的解决方案。

灵感的来源

最近bbc.co.uk推出(测试版本)最新的版本,整个布局干净、宽敞以及他们的Card UI更是实现的完美无缺。咱们先忽略标题部分,这样看起来会更棒。

Card

顶部Card排列和布局采用的是Flexbox,但我们将使用Grid来修改这个布局。

Card

注意:要想看到接下来介绍的效果,你需要一个支持Grid的浏览器,在接着阅读下面的内容之前,你可以先看看这些信息

Card的HTML结构

把所有Card放到一个Grid容器里<div class="band">,网格项目(也就是每个Card)都用<div class="item">以及一些链接等信息:

<div class="band">

  <!-- grid item, containing a card -->
  <div class="item-1">
        <a href="" class="card">
          <div class="thumb"></div>
          <article>
            <h1>Post title</h1>
            <span>Author</span>
          </article>
        </a>
  </div>

  <!-- grid item, containing a card -->
  <div class="item-2">
  ...
  </div>

  <!-- grid item, containing a card -->
  <div class="item-3">
  ...
  </div>

</div>

band里可以放置尽可能多的Card,只要你喜欢,文中的示例使用的是七个Card。每个Card都有一个缩略图<div class="thumb">,给它设置一个背景图片。然后有一个<article>,它包含一个<h1>、一个作者信息<span>,甚至一些描述信息<p>

网格基础知识

现在,让我们给Card设置一些样式,让它在网格中排列。

如果你从未接触过CSS Grid相关知识,你可以点击这里了解CSS Grid布局中的基础知识,能更好的帮助你理解下面的内容。

首先实现移动端上的布局,所以给网格容器一个宽度,并且让它居中对齐,样式代码如下:

.band {
    width: 90%;
    max-width: 1240px;
    margin: 0 auto;

    display: grid;

    grid-template-columns: 1fr;
    grid-template-rows: auto;
    grid-gap: 20px;
}

在这里要我们要说明几点,这里通过display:grid.band声明为一个网格容器,然后将grid-template-columns的值设置为1fr,让浏览器自己去计算每个Card占据容器多少宽度。这里只声明了一个fr,表示网格项目会自动填充容器。

然后声明grid-template-rows: auto;,这实际上是默认值,其实可以省略它,并且意味着行的高度将完全是依靠内容来确定的。

最后定义一个grid-gap值为20px,这个是用来确定行和列的间距。

媒体查询设置

在更宽度的视窗(这个示例设置的是500px,这是一个任意值,你可以根据你自己需求来设置),将更改grid-template-columns的值为两个1fr。也就是表示每行显示两个Card,每列都是可用宽度的一半。

@media only screen and (min-width: 500px) {
  .band {
    grid-template-columns: 1fr 1fr;
  }  
}

接下来在更宽的视窗下,继续修改grid-template-columns的值,让每行显示四张Card:

@media only screen and (min-width: 850px) {
  .band {
    grid-template-columns: 1fr 1fr 1fr 1fr;
  }
}

这里还可以通过repeat(4, 1fr)来替代1fr 1fr 1fr 1fr。有关于1fr更多的介绍,可以阅读《CSS Grid布局:列和间距》一文。

此时看到的效果如下:

美化Card

这样我们就具有一个固定的网格布局,如果你是Brutalism的忠实粉丝,你可能到这一步就觉得OK了,但对于其他人而言,希望我们把Card效果做得更逼真一些。

从这开始来美化Card:

.card {
    background: white;
    text-decoration: none;
    color: #444;
    box-shadow: 0 2px 5px rgba(0,0,0,0.1);
    display: flex;
    flex-direction: column;
    min-height: 100%;
}

这样可以看到具有一些基本样式风格的Card:一个白色的背景、没有下划线的文本、使用box-shadow实现的一个灰色的阴影效果。

接下来使用display:flex把Card声明为一个Flexbox容器。这很重要,我们将使用Flexbox对Card内容在垂直方向排列。因此需要指定flex-direction的值为column。最后声明min-height: 100%;以便所有Card的高度能和父容器高度一样(也就是网格项目)。此时看到的效果如下:

Card

悬浮效果

在进一步介绍Flexbox这前先做一些其他的改进。添加position:relative;,以便鼠标悬浮在Card上时有稍微的移动效果:

position: relative;
top: 0;
transition: all .1s ease-in;

鼠标悬浮时,Card稍微往上移一点,并且阴影更明显:

.card:hover {
    top: -2px;
    box-shadow: 0 4px 5px rgba(0,0,0,0.2);
}

文本排版

接下来给文本做一些排版处理,改变文本的颜色和间距。

.card article {
    padding: 20px;
}

/* typography */
.card h1 {
    font-size: 20px;
    margin: 0;
    color: #333;
}

.card p { 
    line-height: 1.4;
}

.card span {
    font-size: 12px;
    font-weight: bold;
    color: #999;
    text-transform: uppercase;
    letter-spacing: .05em;
    margin: 2em 0 0 0;
}

此时你看到的效果就像下面这样:

添加缩略图

每个缩略图使用的是背景图片,所以在<div class="thumb">上添加一些行内样式:

<div class="thumb" style="background-image: url(thumb.jpg);"></div>

这种做法并不是很好的处理方式,更建议给div.thumb添加一个类似data-thumb这样的自定义属性,然后使用JavaScript做处理,将其变成div.thumb的背景图片。

现在,为了确保背景图片尺寸和div.thumb尺寸相同,需要给.thumb添加一些处理背景图片的样式代码:

.card .thumb {
    padding-bottom: 60%;
    background-size: cover;
    background-position: center center;
}

好极了,此时你看到的效果如下:

使用Flexbox优化Article样式

现在我们要让作者姓名放到Card底部,不管它上面有多少内容。那么这个时候Flexbox的优势就显现了:

.card article {
    padding: 20px;
    flex: 1;

    display: flex;
    flex-direction: column;
    justify-content: space-between;
}

我们使用flex:1让Flex项目(它仍然是原始Flex容器的子项)占用所有可用空间。

然后,我们声明.article也是一个Flex容器,并且再次声明flex-direction:column;.article所有子元素在容器中垂直排列。最后设置justify-content:space-between;让所有Flex项目沿着轴均匀地分布,其具有相等的间距。

这是很强大的一特性,但总是有些地方看起来怪怪的,比如段落顠在Card中间。

Card

为了让它们排列变得更整齐化,给段落添加flex-grow:1;(或者简单地设置flex:1;),让段落填充所有剩余可用的垂直空间,这样文本就能很好地实现顶部对齐。

.card p { 
    flex: 1; /* make p grow to fill available space*/
    line-height: 1.4;
}

效果是不是变得更精美了。

Card

改变网格布局

很多时候,在所有的Card中有那么一张与众不同(称之为特色卡),这个时候Grid的优势就体现出来了。Grid允许我们完全改变布局,将我们特色卡放置在网格任意位置和改变成我们想要的尺寸大小。比如这个示例,把特色卡宽度变成两列宽度(除了移动端)。

只需要在第一个媒体查询中这样做:

@media only screen and (min-width: 500px) {

    ...

  .item-1 {
    grid-column: 1/ span 2;
  }

}

在《CSS Grid布局:网格区域》一文中介绍过,超过500px,第一个网格项目从第一个网格线开始,跨越两个轨道。其它的网格项目就会自动落到后面。整个效果如下图所示:

Card

在这个媒体查询中,也将设置不同的font-size,让我们的特色卡标题有不同的字体大小。

总结

这是CSS Grid和Flexbox混合在一起实现一个完美布局,这也是一个很好的练习案例。先使用CSS Grid实现主要的二维布局,然后使用Flexbox处理Card内元素在垂直方向的排列布局。很好玩吧。如果你感兴趣,欢迎自己动手尝试,亲自体验一下。

扩展阅读

本文根据@Ian Yates的《Solving Problems With CSS Grid and Flexbox: The Card UI》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:https://webdesign.tutsplus.com/tutorials/solving-problems-with-css-grid-and-flexbox-the-card-ui--cms-27468

大漠

常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等前端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《图解CSS3:核心技术与案例实战》。

如需转载,烦请注明出处:http://www.w3cplus.com/css3/solving-problems-with-css-grid-and-flexbox-the-card-ui.html

返回顶部