Within Drupal 10 core, we’re implementing a new auto-filling CSS Grid technique that I think is cool enough to share with the world.
The requirements are:
- The user specifies a maximum number of columns. This is the auto-filling grid’s “natural” state.
- If a grid cell goes under a user-specified width, the auto-filling grid will readjust itself and decrease the number of columns.
- The grid cells should always stretch to fit the auto-filling grid container’s width, no matter the column count.
- All of this should work independent of viewport width and should not require JavaScript.
The auto-filling CSS Grid in action
Here’s how the resulting auto-filling CSS grid behaves when it is compressed by the draggable div element to its left.
Here’s the code
If you’re not looking for the theory behind the auto-filling grid, and just want to copy/paste code, here you go!
.grid-container {
/**
* User input values.
*/
--grid-layout-gap: 10px;
--grid-column-count: 4;
--grid-item--min-width: 100px;
/**
* Calculated values.
*/
--gap-count: calc(var(--grid-column-count) - 1);
--total-gap-width: calc(var(--gap-count) * var(--grid-layout-gap));
--grid-item--max-width: calc((100% - var(--total-gap-width)) / var(--grid-column-count));
display: grid;
grid-template-columns: repeat(auto-fill, minmax(max(var(--grid-item--min-width), var(--grid-item--max-width)), 1fr));
grid-gap: var(--grid-layout-gap);
}
Theory and tools behind the auto-filling CSS Grid
The code above uses several modern CSS tools including CSS Grid’s repeat()
, auto-fill()
, and minmax()
functions, as well as the CSS max()
, and calc()
functions. Here’s how it works.
auto-fill()
function
CSS Grid’s The key to all of this is auto-fill()
. We need each row to fill up with as many columns as possible. For more info on auto-fill
, check out Sara Soueidan’s awesome article on the difference between auto-fill
and auto-fit
, which includes this helpful video showing how it works.
But how to we make sure that it doesn’t fill in too many columns?
max()
function
The CSS That’s where the max()
function comes in! We want each grid cell’s width to max out at a certain percentage, say 25%
for a four-column grid. But, we can’t have it go below the user-specified minimum width.
So, assuming a four-column grid and minimum cell width of 100px
, the max()
function would look something like: max(25%, 100px)
.
However, the 25%
value is still not quite correct because it doesn’t take the grid gaps into account. What we really need is something like this instead:
max(calc(25% - <grid-gap-for-one-cell>), 100px)
We can calc()
-ulate this in CSS! (Who says CSS isn’t programming?)
--gap-count: calc(var(--grid-column-count) - 1);
--total-gap-width: calc(var(--gap-count) * var(--grid-layout-gap));
--grid-item--max-width: calc((100% - var(--total-gap-width)) / var(--grid-column-count));
Now we have another key to making this work! This will tell the grid cell to go to its maximum width — which takes into account the user-specified columns) — but will never go under 100px
.
max(100px, var(--grid-item--max-width))
Learn more about the max()
function with Chris Coyier’s article on the CSS min()
,max()
, and clamp()
functions.
minmax()
function
CSS Grid’s We’re getting close, but there’s one key ingredient that’s missing: The grid doesn’t always stretch to its parent’s container’s width.
This is exactly what the minmax()
function is designed to do. The following CSS will set the minimum width to the <grid-item-width>
, and if it has room, it’ll stretch all the cells out equally to fit the parent’s width!
minmax(<grid-item-width>, 1fr)
Let’s put it all together and make some magic!
Using the tools above, we can put together this magic bit of code that does exactly what we want!
--gap-count: calc(var(--grid-column-count) - 1);
--total-gap-width: calc(var(--gap-count) * var(--grid-layout-gap));
--grid-item--max-width: calc((100% - var(--total-gap-width)) / var(--grid-column-count));
grid-template-columns: repeat(auto-fill, minmax(max(var(--grid-item--min-width), var(--grid-item--max-width)), 1fr));
CSS is fun!
CSS has really come a long way. I had a lot of fun working on this, and I’m so happy that use-cases like this are now possible without the use of JavaScript.
Special thanks to Andy Blum, who suggested auto-fill()
over auto-fit()
. Also, an extremely special thanks to all of the implementors and spec writers who make advanced functions like this standardized and possible.
Cool setup, really shows the power of grid layout when used with the new functions.
I may be missing something, but the name
--grid-item--max-width
seems misleading. It’s not a maximum value, it’s a minimum value like the other one – an item never becomes narrower than either of them.yeah, I can see that point of view. But it’s not really the minimum possible width. It’s the “normal” width. I just called this one
--grid-item--max-width
because it’s not hard coded to a set value inpx
(or whatever).You’re understanding it properly, it’s just that naming this is hard :)
THANKYOUUUU!!!
SERIOUSLY THANK YOUUUU!!
Ha! This made me smile. You’re welcome!
Really cool! Using CSS grid with calc makes it really powerfull.
I did a similar thing with the Fylgja auto-grid.
I’ve never heard of Fylgja… but it does look really similar (and cool!). Looking at the source, it’s almost the same, with the exception that they don’t support the max number of columns.
Cool! But check this out if you don’t want to specify just a maximum columns number, but also a minimum columns number using only CSS.
Explanation:
https://stackoverflow.com/questions/52417889/setting-minimum-and-maximum-number-of-columns-using-css-grid/52418341/#69154193
Demonstration:
I have also a similar approach to define a grid with only a maximum columns number:
I am currently experimenting with a grid system based on similar idea. Here is a documentation page for it: https://fluid-grid.com/
Just check it, I am currently collecting feedback ;)
This is so cool! The documentation and interactive examples are very nice. I’m having fun with this and hope to learn a lot. Thanks for putting it together!
Hi there, cool thing! I just tried it out and I think I have found a little issue: I always get one column less then I specify.
So
--grid-column-count: 4
gives me 3 columns,--grid-column-count: 3
gives me 2 columns, and so on.Scrap my previous comment – I was overwriting gap later in my css. That was causing my wrong column-count. Great tutorial!!
Really cool.
But does anyone have the same problem with gulp-sass:
Error: “var(–grid-item–min-width)” is not a number for `max’
Try Sass Interpolation (https://sass-lang.com/documentation/interpolation). Basically, wrap it in
#{...}
In case anyone else has this problem, it can be fixed by capitalizing the
max
(ormin
) keyword.So if this fails:
minmax(max(var(--grid-item--min-width)
It should work if you write it like this:
minmax(Max(var(--grid-item--min-width)
I found the answer in this thread on GitHub.
This is fantastic and fit my use case perfectly on a project I’m working on. Thank you so much for sharing this.
Yay!
Great post! I love how dynamic this solution is.
How would one set the .grid-item height to be equal to the current column width? I’d like to make the grid items into perfect squares, but I can’t quite figure how I could use the function to find the column width in pixels and set the .grid-item height to it.
My current attempts have focused on setting the row height to be equal to the column width, but all have been unsuccessful.
Any help would be much appreciated!
You don’t need to hook into the grid system to do so. To make perfect squares, use the CSS
aspect-ratio
property. https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratioOh wow, I was over engineering the solution. Thank you!
I just finished reading the “Making Rainbows with CSS” article on your site. I hope to see more of your content on CSS-tricks in the future.
Fantastic writeup! The part I’m not seeing is how
--grid-column-count
gets updated. All the math hinges on this value being dynamic, yes?Thanks!
Yep,
--grid-layout-gap
,--grid-column-count
, and--grid-item--min-width
can all be dynamic.You’ll need either server-side templating or JavaScript to inject the properties as an inline style to the
.grid-container
element (or one of its ancestors).Checkout my codepen at https://codepen.io/mherchel/pen/xxXXbog for an example on how to inject the inline style via JavaScript.
Woa awesome! Thanks for that tipp, thats defenitely out of my league ;)
best regards
Thank you! Grid is fun. Play around and you can figure cool things out :)
Is there any way to force the number of columns to 1, 2 or 4. I’m working on a layout, with bootstrap cards, where I want the maximum columns to be 4. 1 or 2 columns looks fine on mobile or small screens, but it looks bad when it hits 3 columns.
I suppose I could use media queries but that feels like it defeats the purpose of a flexible layout like this.
Awesome, exactly what I needed! Thanks for the great tutorial and sharing your code.
great post!! Is it possible to adapt minimum width of every item to content width?
This is so cool, thank you so much!
One small thing I noticed is that, since the value setted for
--grid-item--min-width
is fixed (e.g.: 250px), an overflow on the X axis can happen when the viewport becomes very narrow.To prevent this you could set your
--grid-item--min-width
to the minimum between user’s input value and 100%, so:What do you think? ☺️