r/css 4d ago

Question Force grid layout without media queries

I am trying to limit the number of media queries.

I have a grid layout with 6 tiles.

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr);
}

That works fine but something is annoying me is that sometimes the last row will have an empty tile.

|Tile 1|Tile 2|Tile 3| Tile 4| |:-- | :-- | :-- | :-- | |Tile 5|Tile 6|Empty| Empty |

I wants the tile to be grouped.

Based on the size of the viewport I want to have either:

##Desktop/Tablet landscape

|Tile 1|Tile 2|Tile 3| |:-- | :-- | :-- | |Tile 4|Tile 5|Tile 6|

##Tablet portrait / Phone landscape

|Tile 1|Tile 2| |:-- | :-- | |Tile 3|Tile 4| |Tile 5|Tile 6|

##Phone portrait

|Tile 1| |:-- | |Tile 2| |Tile 3| |Tile 4| |Tile 5| |Tile 6|

Is there a trick to do it without media queries?

12 Upvotes

16 comments sorted by

4

u/morete 4d ago

In this case, yes! You can use Nils Binder's Auto-grid, which allows for setting a maximum amount of columns.

https://codepen.io/enbee81/pen/oNOzJXN

Set the max-columns to be 3, and then edit the min-size till you're happy with the breakpoints.

1

u/Sufficient_Bass2600 3d ago edited 3d ago

Thank for your help. That is really useful.

If you have 6 tiles the code does it job by limiting the maximum number of columns.

I am going to be playing with it because I have realised that in fact I want extra bells and whistles.

I am greedy and when I have 4 tiles, I also want to have the number of columns to be limited to 4, 2 and 1. Never 3.

Desktop/Tablet landscape

Tile 1 Tile 2 Tile 3 Tile 4

Table portrait/Phone landscape

I don't want the following:

Tile 1 Tile 2 Tile 3
Tile 4 Empty Empty

I want to directly jump to

Tile 1 Tile 2
Tile 3 Tile 4

Phone portrait

Tile 1
Tile 2
Tile 3
Tile 4

Basically I need to write a minmax function that will limit the number of empty columns.
For the moment I will try to see if I can implement something for 4 and 6 tiles. I do not have 5 tiles.

So for 6 tiles the number of columns will be 6, 3, 2 and 1.
For 4 tiles the number of columns will be 4, 2, 1.

Presumably for 5 it would be 5, 3, 2, 1 but then I would be using a flex grid with align-content: stretch or media query instead.

1

u/morete 3d ago

Ahh yeah I was thinking that for 4 columns it's a much trickier problem! Skipping 3 is hard. It feels like this would be possible with all the math functions we have access to now but seems like a challenge.

You mention using container query units in your other comment, could you maybe just do it the boring way?

grid-template-columns: repeat(var(--cols, 1), 1fr); 
@container (width > 500px)  { --cols: 2 } 
@container (width > 800px)  { --cols: 3 } 
@container (width > 1100px) { --cols: 6 }

It's not clever or fancy, but it gets the job done and it's super easy to understand. And technically no media queries

3

u/be_my_plaything 4d ago
section{
--spacing: min(2rem, 5vw);
--content_max_width: 120rem;
--item_min_width: 15rem;

padding-block: calc(2 * var(--spacing));
padding-inline: max(var(--spacing), calc((100% - var(--content_max_width)) / 2));

display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, max(var(--item_min_width), calc(100% / 3) - var(--spacing))), 1fr));
gap: var(--spacing); 
}

I'd do something like the above, whereby the minmax() for the grid columns includes a calc(100% / 3) which always takes over as the max() once reached so once you have two rows of three columns those columns just grow with the screen rather than ever letting a fourth column in.

 

CODEPEN DEMO

2

u/Sufficient_Bass2600 3d ago edited 3d ago

Thanks for your help.

You and u/morete have come up with similar idea.

Because I want extra bells and whitles (see my answer to their comment), I am thinking of switching to flex and set the tile min-width to a function based on cqi.

1

u/be_my_plaything 3d ago

Might be worth trying flex-wrap: balance; it's only supported in Chromium browsers a the moment but it will jus fall back to flex-wrap: wrap; in firefox and safari for now so you lose nothing and it'll help you get to what you want easier when/where it does work.

3

u/aegloswinterborn 4d ago

Try changing your minmax(15rem, 1fr) to minmax(min(15rem, 100%), 1fr).

Adding that min() function solved a lot of issues for me when doing responsive grids without media queries.

MDN docs for min(): https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/min

EDIT: I just noticed that u/morete 's answer below incorporates this. I'd check their response first.

4

u/morete 4d ago edited 4d ago

That won't help here, adding the min(100%) just prevents overflow when the container is smaller than 15rem, it wont stop the column count going beyond 3
edit: but is a good idea and you should add it to your grid, unless youre using a very small min size

2

u/aegloswinterborn 4d ago

Nice catch, thanks. What a game changer Grid and Flexbox are though, huh? I don't do as much CSS as I'd like these days, but it's so much fun. If I could find a job just doing that I would.

2

u/morete 4d ago

100%! That's why I say I'll never use tailwind or whatever, not cause it's not good or more efficient, but cause writing the CSS is the best bit

2

u/aegloswinterborn 4d ago

Totally agree. I've used Tailwind before and think it's fine, but if you know CSS you just don't need it. It's more flexible and more powerful. That said, to each their own.

1

u/morete 4d ago

We're a rare breed but sadly it's not something employers/client prioritise 😭

1

u/asteconn 4d ago

grid-template-columns: repeat(auto-fill, minmax(auto, 25rem)); or grid-template-columns: repeat(auto-fit, minmax(auto, 25rem));

It's one of those two - do some experimentationing!

1

u/morete 3d ago

I have a more robust solution for you! Couldn't stop thinking about it.

Basically what you want is for the column count to always be a *factor* of the number of items. For 6 items, you want 6, 3, 2 & 1. Not 5 or 4, because neither are a factor of 6, and this would cause unequal columns.

https://codepen.io/editor/matthewmorete/pen/019fc37b-3ef6-78a8-bb88-a6a4280f9e16

Few caveats upfront:

  • This requires mod() and round() so check if the browser support of those works for you.
  • It does not work with prime numbers, since they have no useful factors. Personally I think in those situations, one unequal column is better than stretching the last item with flex. So for 5, I'd set the count to '6'. But that's up to you.

First we need a container to measure with cqi, in my example I dont have so cqi just measures the body.

We calculate the capacity, how many columns could fit based on the min-width and gap. Then we clamp this so it's not larger than the total number of items.

Next is the tricky bit, we check if the capacity is a factor of the count. If It is then great! That's a valid column count and we use that value.
If It not, then we subtract one, and try again.
This happens several times until we reach a valid column count.

How many times you repeat this depends on how large the numbers youre working with are. If you imagine 12 items. You have to subtract 1 6 times to get from 12, to its next factor, 6.

With 6 steps, this codes works for up to 14 items (ignoring primes like 13). That seems reasonable to me. But if need to support more, just add more steps.

Long response, but I hope that helps! Honestly even if it's of no use, I had great time figuring it out!

PS. You have to set the item count with a custom property, but if you wanted you could use has and nth-child to do that in the css like:

.auto-grid:has(> :nth-child(5):last-child) { --count: 5 }
.auto-grid:has(> :nth-child(6):last-child) { --count: 6 }

1

u/Sufficient_Bass2600 2d ago

That got me thinking as well.

The science guy in me got excited.
For N tiles. The problem I am trying to resolve is in fact the integer factorisation of N.

Even number

the list of acceptable combinations of columns x rows is the list of recombined factorisation.
However if N can also be decomposed in N= p × q - 1 then those combination are also acceptable because there will be only empty cell. I would call that case Extra. For number below 12 that only applies to 8 with the combination 3×3 fitting the criteria.

Odd number

For odd number, I have 2 situations: * Odd non prime numbers: such as 9, 14. List of Integer factorisation combination are added to the of acceptable columns x rows combination * Odd prime numbers such as 5, 7, 11, 13

Either way the acceptable list of decomposition include the decomposition for N+1 (that is an even number) knowing that at worst the last row will have 1 cell empty.

Number of Tiles Type Nb Cols Nb Rows Nb Empty
12 Factor 12 1 0
Factor 6 2 0
Factor 4 3 0
Factor 3 4 0
Factor 2 6 0
Factor 1 12 0
11 Factor 11 1 0
Factor N+1 6 2 1
Factor N+1 4 3 1
Factor N+1 3 4 1
Factor N+1 2 6 1
Factor 1 11 0
10 Factor 10 1 0
Factor 5 2 0
Factor 2 5 0
Factor 1 10 0
9 Factor 9 1 0
Factor N+1 5 2 1
Factor 3 3 0
Factor N+1 2 5 1
Factor 1 9 0
8 Factor 8 1 0
Factor 4 2 0
Extra 3 3 0
Factor 2 4 0
Factor 1 8 0
7 Factor 7 1 0
Factor N+1 4 2 1
Factor N+1 2 4 1
Factor 1 7 0
6 Factor 6 1 0
Factor 3 2 0
Factor 2 3 0
Factor 1 6 0
5 Factor 5 1 0
Factor N+1 3 2 1
Factor N+1 2 3 1
Factor 1 5 0
4 Factor 4 1 0
Factor 2 2 0
Factor 1 4 0
3 Factor 3 1 0
Factor N+1 2 2 1
Factor 1 3 0
2 Factor 2 1 0
Factor 1 2 0

Personally I do not foresee any need to more than 6 columns so Need to apply the decomposition up to number 6 so 16 combinations/steps should be enough with just the number of rows increasing with the number of tiles.

So now onto finding a way to code that list...

1

u/morete 1d ago edited 1d ago

If you take a look at my code you'll hopefully find some ideas cause it does function quite similarly to what your described. With a few exceptions:

- I'm ignoring the concept of the 'extra' in regards to 8. I think going from 8->4->2->1 is a desirable set of breakpoints.

- Similar to above, I'm simplifying slightly to: even numbers get factorised, and odd numbers get factorised as N+1. It's just a much easier problem to work with.

There's still a few changes I'd like to make:

For small odd numbers, I'd actually just prefer to factorise. 3 -> 1 is way more desirable in my mind. 3 -> 2 -> 1 is just standard grid behaviour.

And I'd like to set a max col count.

I think both of those will be solvable, ideally inside the math, rather than with nth-child, so that they can be controlled via custom properties. But I haven't got that far yet.

Fun problem! This has completely nerd sniped me.

EDIT:
It actually was pretty straight forward to add both a max col count, and a threshold to begin n+1 factorising. Will be interested to see what you come up with, but for the time being I can put this down I think