使用CSS Grid和Flexbox制作Card
“Card”这样的UI组件在现在的Web中经常可见,但我们制作这方面的UI组件方式仍然受到一定的限制。直到现在,通过CSS Grid和Flexbox的组合,可以使卡整齐对齐,响应性更好,并适应其中的内容。接下来看看我们是如何制作的。
我们将要做的
我们将要做的一个Card UI组件(点击这里可以全屏查看效果,这样你会看得更清楚)。
上面的效果,在各种断点下(缩小浏览器屏幕),Card的布局将会像下面那样进行改变:
CSS Grid vs Flexbox
Flexbox在CSS中出现,可以说让全世界的Web开发者欢呼起来,终于有一个布局模块来解决CSS 浮动布局给我们带来的种种麻烦与拙折。但事实并非如此。Flexbox最适合是沿单个轴分布元素,要么沿水平方向或者沿垂直方向排列元素。也就是说要使用Flexbox来构建真正的流体网格布局还是非常困难的。
然而,CSS Grid旨在跨越两个轴(或维度)排列元素,也就是说水平和垂直方向都可以排列元素。在这篇文章中,通过使用CSS Grid来实现文章开头展示的Card布局效果,这也是这篇文章的真正的目的。让我得到一个真正的解决流体网格布局的解决方案。
灵感的来源
最近bbc.co.uk推出(测试版本)最新的版本,整个布局干净、宽敞以及他们的Card UI更是实现的完美无缺。咱们先忽略标题部分,这样看起来会更棒。
顶部Card排列和布局采用的是Flexbox,但我们将使用Grid来修改这个布局。
**注意:**要想看到接下来介绍的效果,你需要一个支持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的高度能和父容器高度一样(也就是网格项目)。此时看到的效果如下:
悬浮效果
在进一步介绍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中间。
为了让它们排列变得更整齐化,给段落添加flex-grow:1;
(或者简单地设置flex:1;
),让段落填充所有剩余可用的垂直空间,这样文本就能很好地实现顶部对齐。
.card p {
flex: 1; /* make p grow to fill available space*/
line-height: 1.4;
}
效果是不是变得更精美了。
改变网格布局
很多时候,在所有的Card中有那么一张与众不同(称之为特色卡),这个时候Grid的优势就体现出来了。Grid允许我们完全改变布局,将我们特色卡放置在网格任意位置和改变成我们想要的尺寸大小。比如这个示例,把特色卡宽度变成两列宽度(除了移动端)。
只需要在第一个媒体查询中这样做:
@media only screen and (min-width: 500px) {
...
.item-1 {
grid-column: 1/ span 2;
}
}
在《CSS Grid布局:网格区域》一文中介绍过,超过500px
,第一个网格项目从第一个网格线开始,跨越两个轨道。其它的网格项目就会自动落到后面。整个效果如下图所示:
在这个媒体查询中,也将设置不同的font-size
,让我们的特色卡标题有不同的字体大小。
总结
这是CSS Grid和Flexbox混合在一起实现一个完美布局,这也是一个很好的练习案例。先使用CSS Grid实现主要的二维布局,然后使用Flexbox处理Card内元素在垂直方向的排列布局。很好玩吧。如果你感兴趣,欢迎自己动手尝试,亲自体验一下。
扩展阅读
- Understanding CSS Grid Layout
- Should I use Grid or Flexbox?
- Designing Card-Based User Interfaces
- bbc.co.uk beta homepage
- Quick Tip: Add a @supports CSS File to Your CodePen Demos
- CSS Grid
- Flexbox
- 说说CSS中的@supports
本文根据@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。Kobe 11 ELite Glowing