Skip to content

Fluid Grids

Perhaps the most famous CSS Grid snippet looks like this:

Code Playground

Result

Enable “Tab” for indentation

Try resizing the RESULT pane in full-screen mode to see how it works.

Essentially, this handy-dandy “World Famous” grid snippet creates a grid with a dynamic number of columns. Each column will be the same width, and it'll try and pack as many in as it can while staying above a specified minimum (in this case, 150px).

It's useful when we have a list of items that we want to render, like a group of cards. We want them to be uniform, and to tile nicely no matter what the screen size is, or how many items we have.

It's also a very complicated snippet, one which leverages many advanced aspects of the Grid layout mode. In this lesson, we're going to break it down, and learn how each piece works. This is really useful, as it gives us a whole set of tools we can use in other situations!

Clamping with minmax

Earlier, we saw how the “min”, “max”, and “clamp” functions can be used to constrain a specific value between a lower and/or upper bound.

CSS Grid has a similar function: minmax.

Here's an example:

Code Playground

Result

Enable “Tab” for indentation

Here's my mental model for what's going on here: we want to have two equal-width columns (both 1fr), but our first column should never shrink below 50px, while the second one shouldn't shrink below 250px.

It's essentially a way to set a min-width on our grid columns.

Similarly, minmax allows us to set a min-height when used with grid-template-rows.

One quick gotcha: the flexible unit has to come last. minmax(1fr, 250px) is invalid, because 1fr isn't a valid "minimum" value.

Generating columns with auto-fill

Earlier, we saw how the repeat function can save us a bit of typing:

.grid {
grid-template-columns: repeat(7, 1fr);
/*
...is equivalent to:
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
*/
}

repeat has another trick up its sleeve, though: it accepts special keywords in addition to straight-up numbers.

Let's say we have some cards that are exactly 150px wide. We want to fill the grid with as many 150px-wide columns as possible.

We can do that with auto-fill:

Code Playground

Result

Enable “Tab” for indentation

Inspect this grid in your browser's devtools, and resize the RESULT pane. Notice that as the available space increases, so too does the number of columns.

When the available space isn't a perfect multiple of 150px, we have some leftover space. We can control how the columns are aligned with justify-content:

Code Playground

Result

Enable “Tab” for indentation

auto-fill allows us to implement fluid principles in CSS Grid. As we saw in a previous module, fluid design uses constraints that shift automatically based on the container size, rather than specifying explicit behaviour at particular viewport sizes.

Putting them together

Alright, so we know that minmax lets us clamp a value to a specific range, and we know that repeat takes a special value, auto-fill, which generates a dynamic number of columns.

When we put them together, we recreate the World Famous Grid Snippet:

Code Playground

Result

Enable “Tab” for indentation

Here's how this algorithm works:

  1. Figure out how many columns we can fit at the minimum acceptable size, 150px.
  2. Scale up each column so that the entire horizontal space is filled

If the container is 450px wide, we can fit exactly 3 columns at 150px, and there's no leftover space.

If the container is 480px wide, we can still only fit 3 columns, but there's 30px of leftover space. Each column gets 10px wider, so that we have 3 columns that are 160px wide, perfectly filling the 480px-wide container.

Note: This is a slight oversimplification. The real algorithm subtracts additional space like gap or padding from the calculations, and supports having different columns of different proportions. But this is the basic idea!

auto-fill vs. auto-fit

In addition to the auto-fill keyword, there's also an auto-fit keyword.

Most of the time, these two properties work exactly the same way, and can be used interchangeably. There is one significant difference, though.

Let's suppose that we're rendering a dynamic list of data. We might have 20 items, or 40 items, or 2 items.

In this particular case, we only have 2 items, but they're rendered in a very-wide grid. Let's say we have space for 6 columns

What should happen? Should we create 4 additional empty columns, so that our 2 items stay approximately the same size as they would be if we had 20 items? Or should we create 2 super-wide columns that span the entire area?

This is the fundamental difference between auto-fill (lots of empty columns) and auto-fit (stretched ultra-wide columns).

Try it for yourself in this snippet:

Code Playground

Result

Enable “Tab” for indentation

Personally, I almost always use auto-fill. since it ensures consistency in my grid no matter how many items I have. But if you want to make sure the elements span 100% of the available space, auto-fit has you covered.

For more information on this distinction, check out this fantastic article on the topic by Sara Soueidan.

Responsive tweaks

So far, we've been working with cards that have a very small minimum width, 150px.

Notice what happens if we choose a larger minimum width:

Code Playground

Result

Enable “Tab” for indentation

We've declared that our columns should never shrink below 400px wide, which causes a horizontal overflow on smaller screens!

How can we solve for this? Well, there are two options.

The responsive approach

We can always use a media query that changes the grid structure when we get below the minimum threshold:

Code Playground

Result

Enable “Tab” for indentation

When our viewport is smaller, we'll stick with a much-more-simple grid layout: a single column. We'll only use our World Famous Grid Snippet on larger screens, when we have enough space for it.

Note that the media query has a 50px buffer. I flip the switch at 450px, even though our minimum card width is 400px. This adds enough space for padding (32px total, 16px on each side), and a scrollbar (10-15px depending on operating system).

The fluid approach

Alternatively, we can add yet another constraint to our snippet:

Code Playground

Result

Enable “Tab” for indentation

Yikes, what a snippet 😬. Let's talk through what's happening here.

The first part to get evaluated is this:

  • min(400px, 100%)

100% refers to the .grid element's width. If we're viewing this on a large monitor, .grid might be 800px wide, so 100% resolves to 800px.

min() picks the smaller of the two values, so on a large monitor, 400px is returned from this expression.

On a smaller screen, however, 100% might only be 250px. In this case, 100% is returned, since it's smaller than the alternative 400px option.

Once we've figured out which value is smaller, the resulting value is plugged into the “World Famous” snippet:

/* On large screens: */
.grid {
grid-template-columns: repeat(
auto-fill,
minmax(400px, 1fr)
);
}
/* On small screens: */
.grid {
grid-template-columns: repeat(
auto-fill,
minmax(100%, 1fr)
);
}

So which of these two approaches is better?

The unpopular-but-true answer is that it depends!

When I work on solo projects, I tend to favor fluid approaches, because I've been using these techniques for a while, and I'm comfortable with them.

When working on a team with a bunch of other devs, though, I might reach for the responsive approach instead. Most JS devs aren't super comfortable with these advanced CSS techniques (which is in part why I built this course!), and I don't want to write code that is inscrutable to my coworkers.

It's similar to advanced JS techniques, like recursion. Recursion is a powerful tool, but in certain situations, it might be wiser to use a more-straightforward iterative approach, so that junior teammates can understand and contribute to the code.

Exercises

World-Famous with no copy/paste

For this first exercise, let's implement the World-Famous Grid Snippet from scratch, without copy/pasting.

I know it's tempting, but by writing it out manually, you'll be reinforcing the ideas. We want to think of this not as a magical incantation, but as a combination of well-understood tools that combine to create the desired layout.

Update the playground below to use the World-Famous Grid Snippet with the criteria listed in the comments:

Code Playground

Result

Enable “Tab” for indentation

Solution: