Solving Problems With CSS Grid and Flexbox: The Card UI
Card UI designs have seen great success in recent times: by combining CSS Grid and Flexbox, we can make cards which align neatly, behave responsively, and which adapt to the content within them. Let’s see how!
What We’re Going to Build
In this CSS Grid and Flexbox tutorial we’re going to build this card UI (check out the full page version for a clearer idea):
At various breakpoints the card arrangement will change as follows:
1: small, 2: medium, 3: large viewport
CSS Grid vs. Flexbox
When Flexbox hit the CSS scene cries of joy could be heard across the world; finally we had a layout module to solve all our float frustrations. But that wasn’t actually the case. Flexbox works best to distribute elements along a single axis; either horizontally along a row, or vertically as a column. Building a truly fluid grid with Flexbox is difficult.
Grid, however, is intended for laying out elements across two axes (or dimensions); horizontally and vertically.
In this tutorial we’re going to use each one for the purpose it was intended, giving us a really solid solution. Let’s begin!
Card UI Inspiration for This Tutorial
BBC.com has gone clean and spacious with its card UI design. Ignoring the miserable headlines, this looks great.
The top cards are built and aligned across the row with Flexbox, but we’re going to expand on the layout using Grid.
1. Our Card Markup
Let’s begin with a wrapper for our grid <div class="band">
, some grid items to arrange everything <div class="item">
, and some anchors (each anchor will be a card):
<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>
Place as many cards as you like; we’re using seven. Each one has a thumbnail <div class="thumb">
which we’ll give a background image later on. Then there’s an <article>
, which in turn houses an <h1>
, a <span>
for the author, and perhaps even a <p>
for some more information.
2. CSS Grid Layout Basics
We’ll go mobile first here, so the first styles will give our wrapper a width and center it, then set some Grid rules:
.band {
width: 90%;
max-width: 1240px;
margin: 0 auto;
display: grid;
grid-template-columns: 1fr;
grid-template-rows: auto;
grid-gap: 20px;
}
Most importantly, here we’re stating that our .band
will be display: grid;
. We then declare grid-template-columns
of 1fr
, which says that each column will take up a single fraction of those available. We’ve only declared one for now, so each column will fill the whole width.
Then we state grid-template-rows: auto;
. This is actually the default value, so we could have omitted it, and means that the row heights will be determined purely by the content.
Lastly, we define a grid-gap
of 20px
, which gives us our column and row gutters.
Media Query Numero Uno
On wider viewports (500px is completely arbitrary), we’ll change the grid-template-columns
to give us a possible two cards per row. Now there are two columns, each being one of the available two fractions.
@media only screen and (min-width: 500px) {
.band {
grid-template-columns: 1fr 1fr;
}
}
Media Query Numero Dos
Lastly, for larger screens we’ll go with a four column layout.
@media only screen and (min-width: 850px) {
.band {
grid-template-columns: 1fr 1fr 1fr 1fr;
}
}
Here we could equally have written repeat(4, 1fr)
instead of 1fr 1fr 1fr 1fr
. For more information on how the fr
unit works, check out CSS Grid Layout: Fluid Columns and Better Gutters.
So what has that given us?
3. Styling the Cards
That’s given us a pretty solid grid layout, and if you’re a fan of brutalism you may want to keep things like this, but for everyone else let’s make our cards look a little more like cards.
We’ll start with this:
.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%;
}
This gives us some basic styles: a white background, no underline for the text, a gray color, and a neat box-shadow
to give us some depth.
Next we declare the card to be display: flex;
. This is important–we’ll be aligning the contents of the card vertically, using Flexbox. Therefore we also state flex-direction: column;
to give us our vertical axis.
Lastly, we declare min-height: 100%;
so that all the cards fill the height of the parent (our grid item). Good work! That gives us this:
Hover State
Let’s make a couple of other improvements before diving further into Flexbox. Add a position: relative;
and a transition
so that we can move the card on hover:
position: relative;
top: 0;
transition: all .1s ease-in;
Then on the hover, lift the card slightly and make the shadow more pronounced:
.card:hover {
top: -2px;
box-shadow: 0 4px 5px rgba(0,0,0,0.2);
}
Typography
Now we’ll add some general styles to the typography to alter the colors and the spacing.
.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;
}
Here’s what you should have:
Thumbnails
Each thumbnail will be applied as a background image, so we’ll add some markup across the board, like this:
<div class="thumb" style="background-image: url(thumb.jpg);"></div>
Now we’ll make sure the .thumb
divs have some dimensions, and that the background images fill them properly:
.card .thumb {
padding-bottom: 60%;
background-size: cover;
background-position: center center;
}
Good job, that gives us this:
4. Flexbox Article
Now we want the author name to be aligned at the bottom of the card, irrespective of how much content there is above it. This is where Flexbox again comes in:
.card article {
padding: 20px;
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
}
We’re using the shorthand flex: 1;
to state that this flex item (it’s still a child of the original flex container) should grow to take up all the available space.
Then, we declare that the article is a flex container in its own right, and again we’re declaring flex-direction: column;
to give us vertical distribution. Lastly, justify-content: space-between;
states that all the flex items within it should be spread evenly along the axis, with equal spacing between.
That’s all great, but it gives us these strange, wandering paragraphs in the middle of our cards.
To align these properly, let’s add flex-grow: 1;
(or simply flex: 1;
) to them, so they fill all the remaining vertical space, aligning themselves nicely to the top.
.card p {
flex: 1; /* make p grow to fill available space*/
line-height: 1.4;
}
Better!
5. Altering the CSS Grid
At this point we’re pretty much wrapped up, but the one thing Grid allows us to do now is completely change the layout by placing our grid items wherever we like, and at whatever size we like. For this demo, we wanted to make the very first card (let’s call it our “featured card”) two columns wide for anything other than the smallest viewports.
In our first media query, let’s do this:
@media only screen and (min-width: 500px) {
...
.item-1 {
grid-column: 1/ span 2;
}
}
Harking back to our introductory tutorial on grid areas, here we’re saying that, beyond 500px, the first item should begin on grid line 1 and span across two tracks. The rest of the grid items fall into place automatically.
Within this same media query I’ve also bumped the font-size
of the heading in our featured card.
Conclusion
This has been a solid CSS Grid and Flexbox tutorial; Grid handled our main two-dimensional layout, then Flexbox handled the vertical distribution of elements within our cards. Have fun playing around with this card UI design!