Looked into a few questions here but they don't quite solve what I'm looking for.
Say I have a website and I want. On desktop I want this:

This is easy. grid-template-columns: repeat(3, 33%) (basically)
On mobile, however, I want this

What I'm running into is happens before it flips to a single column:

I'm trying clamp(), minmax(), and all sorts of things but nothing ever works as I want. Yes, I can totally use a media query but I was hoping to create a truly fluid grid/flex layout using modern CSS like clamp, grid, minmax, etc so there wouldn't be a need for media queries for basic layout changes.
I know this doesn't work but as a starting point as requested here's a simple version of one of my 100 attempts :) In this version I was trying to use clamp to switch from a repeat(3) to repeat(1).
.wrapper {
display: grid;
gap: 15px;
grid-template-columns: repeat(clamp(1, calc(100% - 500px), 3), 33%);
}
.one {
background: red;
}
.two {
background: green;
}
.three {
background: blue;
}
<div class="wrapper">
<div class="item one"><h3>Example A</h3></div>
<div class="item two"><h3>Example Two</h3></div>
<div class="item three"><h3>Third Example</h3></div>
</div>
I wrote a detailed article with more generic rules: https://css-tricks.com/responsive-layouts-fewer-media-queries/
Here is an idea using max(0px, (400px - 100vw)*1000) inside flex-basis. This formula will eiter give 0px if 100vw (screen size) is bigger than 400px or a very big value in the opposite case giving each element a big flex-basis and create a wrapping. Simply adjust the 400px which play the role of @media (max-width:400px)
.container {
display:flex;
flex-wrap:wrap;
}
.container div {
height:100px;
border:2px solid;
background:red;
flex-basis:max(0px, (400px - 100vw)*1000);
flex-grow:1;
}
<div class="container">
<div></div>
<div></div>
<div></div>
</div>
Using CSS grid it can be like below:
.container {
display:grid;
grid-template-columns:repeat(auto-fill,minmax(clamp(30%, (400px - 100vw)*1000, 100%),1fr));
grid-gap:5px;
}
.container div {
height:100px;
border:2px solid;
background:red;
}
<div class="container">
<div></div>
<div></div>
<div></div>
</div>
A similar question where I am controling the maximum number of columns without media query: CSS grid - maximum number of columns without media queries
We can scale the above solution to consider more complex cases.
Example of moving from 6 to 3 to 1 column:
.container {
display:grid;
grid-template-columns:
repeat(auto-fill,
minmax(clamp(clamp(15%,(800px - 100vw)*1000, 30%), (400px - 100vw)*1000, 100%)
/* if(screen> 800px) 15% elseif(screen> 400px) 30% else 100% */
,1fr));
grid-gap:5px;
}
.container div {
height:100px;
border:2px solid;
background:red;
}
<div class="container">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
To understand the values consider the following ranges:
100%/7 100%/6 100%/5 100%/4 100%/3 100%/2 100%/1
14.3% 16.7% 20% 25% 33.3% 50% 100%
To get 6 columns we need a value in the range ]14.3% 16.7%] (I considered 15%)
To get 3 columns we need a value in the range ]25% 33.3%] (I considered 30%)
We simply avoid the edges to make sure we account for the gaps.
A more generic solution using CSS variables where I will add 0.1% to make sure the value is big enough to get the needed number of column and it can hold the gap.
Let's also add some dynamic coloration (related: How to change the color of <div> Element depending on its height or width?)
.container {
/* first breakpoint*/
--w1:800px;
--n1:6;
/* second breakpoint*/
--w2:400px;
--n2:3;
display:grid;
grid-template-columns:
repeat(auto-fill,
minmax(clamp(clamp(100%/(var(--n1) + 1) + 0.1%, (var(--w1) - 100vw)*1000,
100%/(var(--n2) + 1) + 0.1%), (var(--w2) - 100vw)*1000,
100%), 1fr));
grid-gap:5px;
margin:10px 0;
}
.container div {
height:100px;
border:2px solid;
background:
linear-gradient(blue 0 0) 0 /1% calc(var(--w2) - 100vw),
linear-gradient(green 0 0) 0 /1% calc(var(--w1) - 100vw),
red;
}
<div class="container">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<div class="container" style="--w1:900px;--n1:8;--w2:500px;--n2:4;grid-gap:10px;">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<div class="container" style="--w1:600px;--n1:4;--n2:2;grid-gap:2vw;">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
Using flexbox where we can have a different (probably wanted) behavior where the last item of a row will take all the free space:
.container {
/* first breakpoint*/
--w1:800px;
--n1:6;
/* second breakpoint*/
--w2:400px;
--n2:3;
display:flex;
flex-wrap:wrap;
margin:10px 0;
}
.container div {
height:100px;
border:2px solid;
margin:5px;
flex-basis:clamp(clamp(100%/(var(--n1) + 1) + 0.1% ,(var(--w1) - 100vw)*1000,
100%/(var(--n2) + 1) + 0.1%),(var(--w2) - 100vw)*1000,
100%);
flex-grow:1;
box-sizing:border-box;
background:
linear-gradient(blue 0 0) 0 /1% calc(var(--w2) - 100vw),
linear-gradient(green 0 0) 0 /1% calc(var(--w1) - 100vw),
red;
}
<div class="container">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<div class="container" style="--w1:900px;--n1:8;--w2:500px;--n2:4;">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<div class="container" style="--w1:600px;--n1:4;--n2:2;">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With