In the Cards

So to better understand how to configure and deploy our CSS grid, let’s build a little webpage with a feature that is fairly commonplace lately, thanks to apps like Slack, Twitter, and Trello. The “card” metaphor is a popular way to help visitors think about data as a chunk of information. One of the reasons it works so well in apps like Slack and Twitter environments is because the “data card” translates nicely between screens. If you are building “mobile first” (a phrase that simply means you assume that most of your site’s traffic will come from visitors on their phones, not at their desks) then the “card” metaphor makes much more sense than the one we’ve been clinging to, the “page.”

I’m going to stick with <div>...</div> elements for both the container and all of the cards inside. I’ll create two classes, one for the container called, um, .cardgrid, and one for the cards called .cardcell.

You can name them anything you like, but it is a good practice always to aim for legibility – both for yourself and anyone who might contribute to your site at some point in the future. In this case, by keeping the word “grid” in my container’s name, I make it a bit clearer that this code makes use of grids. The class .cardcell works the same way. Again, not a requirement, but always a good idea.

I’ll start with my HTML, since that should work even without the CSS. Let’s say that my each of my cards will name a painter (perhaps they will be used in a museum setting, for example). Here’s what I end up with.

<div class="cardgrid">
	<div class="cardcell">Van Gogh</div>
	<div class="cardcell">Turner</div>
	<div class="cardcell">Rembrandt</div>
	<div class="cardcell">Seurat</div>
	<div class="cardcell">Cassatt</div>
	<div class="cardcell">Goya</div>
</div>

Again, to be clear: Those six <div class="cardcell">...</div> elements are children of the <div class="cardgrid">...</div> element. The grid depends on that family relationship to work. Cells need to be the immediate children of a shared parent.

Let’s style the .cardcell class first. From inside my CSS document:

html {
	font-size: 25px;
}

body {
	padding: 2rem;
}

.cardcell {
	color: white;
	font-family: Avenir;
	font-weight: 800;
	background-color: skyblue;
	padding: 1rem;
}

A few notes about what you’re seeing above:

  • I’ve set the font-size of <html>...</html> element to 25px;
    • That means an absolute size of 25 pixels is assigned to both HTML element and, wherever possible, all of the descendants of that element. Which means everyone.
  • <body> has its padding (all four sides of that box) set to 2rem;
    • rem is a new-ish unit of measure. It means “root em.”
    • In measuring font-size, for example, em means “of the parent’s font-size.”
    • So font-size: 2em sets my element’s font-size to twice that of its parent.
    • And font-size: 3em sets my element’s font-size to 3x that of the root (almost always <html>).
    • It gets much worse. Just remember that em and rem are both relative units of measure. Type size is tricky because some people have poor eyesight, or tiny monitors: Forcing their font size to an absolute value (e.g., 14px) can be (sometimes) a bad idea, as you may accidentally make your page impossible for them to read. Using relative values instead of absolute ones because then you’re starting from their user settings and scaling up and down from there, rather than starting from your own assumptions. As always, there are tradeoffs.

For more information on units of measure in CSS (which is a huge and daunting topic), see 24ally on the subject of relative units.

  • In .cardcell, we see:

    • font-weight: 800 sets the font to bold, where possible. There are no units on this property, which is… weird. Regular-weight font is typically 400. These change from font-to-font, as well, so don’t worry too much about memorizing something like this. Again, though: In this case, it makes the font appear in bold.
    • Setting padding: 2rem means that the padding in each cell should be 2x that of the padding in the root (<html>).

As always: The best way to learn these things is to play with things like CSS properties' values on codepen.io. Think of it as a research sandbox, and think of yourself as a researcher with a question about CSS units of measure (for example).

Now let’s add some style to our container, and in so doing, finally declare our container as an official CSS grid.

.cardgrid {
  display: grid;
  max-width: 1199px;
  grid-gap: 5px;
}

What is happening here? Obviously, the display property is set to grid, changing the way everything inside that container will behave.

  • max-width: 1199px means that once our container is 1199px wide, it stops expanding. In other words, there is a point past which our container will not grow, no matter how big the visitor makes the window. Think of this as a failsafe mechanism.
  • grid-gap: 5px; Sets the spacing between cells to 5px.

Its All Up To You Now, grid-template-columns

So far, so… meh. There is nothing really interesting yet, right? Wrong! So wrong! Well, a little wrong: It never gets really interesting. But this part is kind of cool.

Let’s add one final property to our grid container, and see what happens. This line looks daunting, but it is really 50% of the reason that grids are useful. I’m including the three lines previous, plus our new line, below:

.cardgrid {
  display: grid;
  max-width: 1200px;
  grid-gap: 5px;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}

Open the pen above in a new window to be able to follow along better!

That is so great. It automatically (1) resizes all of the cells and (2) recalculates the number of cells that can sit on a single row.

To understand more about why this dynamic recalculation is so great, play with the width of your browser’s window while looking at this pen at the codepen site.

  1. Make it narrow so that there is only one column of cards.
  2. Now slowly expand just until you get two columns instead of one.
  3. Move the window back and forth at that point, slowly: See how the width of each card expands, expands, expands, until it hits a certain width, and Shazam!, a new column appears!

But at the same time, the original column and this new column are much narrower than they were before. Try the same thing at the point where two columns grow to three. Same deal. Why?

I’ll finish unpacking this in the next post. But the key is that multilayered value we assigned to grid-template-columns. It is really complex, so let’s just try to understand two components, at first.

grid-template-columns:

  • remember this sets the number and width of grid columns;
  • remember that the width can be absolute (25px) or relative (20%);

grid-template-columns: repeat(a,b)

  • remember that repeat(a,b) means repeat b (a times)

repeat(2,40%) is the same as writing 40% 40%

repeat(5,10px) is the same as writing 10px 10px 10px 10px 10px

But there are at least three important things to know:

  1. Not all columns have to be the same width;
  2. Not all columns have to use the same unit of measure;
  3. I can mix and match my repeat(a,b), too. For example:

grid-template-columns: repeat(2,25%) 20px repeat(3,2em)

is the same thing as

grid-template-columns: 25% 25% 20px 2em 2em 2em

So then, what happens when I say:

repeat(auto-fit, minmax(c,d));

For more on minmax, go to the source: Read the design docs.

That means that it will repeat a certain column width as many times as it can under specific circumstances – where that width can be at least c wide and no more than d wide.

The challenge with understanding this algorithm is it is like a little engine with lots of moving parts. I’ll say more about this next, as it is the core of our Project 3. For now, though, go watch Mozilla’s Jen Simmons, a gifted designer and my absolute favorite thinker about all things web.