Setting minimum and maximum number of columns using CSS Grid
Asked Answered
P

10

62

I have 10 divs (64px x 64px) that I want to display in 5 columns maximum (for Large and Extra Large viewports) and 3 columns minimum (for small viewports like iPhone 7 etc).

I am trying to do this with CSS Grid but not able to do the minimum columns part (It goes to 2 columns even if there is space to fit 3 cols).

body {
  background-color: wheat;
}

.parent {
  display: grid;
  grid-gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(100px, 140px));
  max-width: 800px;
}

.child {
  height: 64px;
  width: 64px;
  background-color: brown;
}
<body>
  <div class="parent">
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
  </div>
</body>

Max width for the grid container is 800px.

Here is the link for Codepen

Edit: If possible, I want to accomplish this only using CSS Grid i.e. without using Media Queries or Flexbox.

Perspicuity answered 20/9, 2018 at 4:59 Comment(2)
so CSS Grid was created to solve problems that created problems itself that requires 100's lines of codes to solve problems that it created that was suppose to solve problems!Helprin
@Helprin why 100's of lines? Check out my solution. It solves the problem with just 2 CSS properties display and grid-template-columns (3 if you need some gap). Then, if you use Sass and want to create a mixin to reuse it anywhere, anytime, it is just up to you, but the mixin has only 3 CSS properties as well ;) And finally, you can add the CSS variables to the mixin which are only 4 CSS extra lines, but there is absolutely no need for it if you don't want to be able to modify the layout at runtime which is probably your case ;)Encumbrancer
E
98

Here are my solutions that work using only CSS Grid.

TL;DR

Solution A - recommended (3 - 5 columns with a min-width of 64px per column)

/* Note the difference with solution B on the grid-template-columns value */

.parent {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%/3, max(64px, 100%/5)), 1fr));
}

.child {
  width: 64px;
  height: 64px;
  max-width: 100%;
}

.parent {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%/3, max(64px, 100%/5)), 1fr));
}

.child {
  width: 64px;
  height: 64px;
  max-width: 100%;
}


/* Styles not related to the goal */
html {
  box-sizing: border-box;
}

*,
*:before,
*:after {
  box-sizing: inherit;
}

body {
  margin-top: 0;
  text-align: center;
}

.parent {
  justify-content: space-evenly;
  margin: 0 auto;
  padding: 5px;
  border: 5px solid dodgerblue;
  background: wheat;
  overflow: auto;
  resize: both;
}

.child {
  margin: 0 auto;
  border: 2px solid brown;
}
<p>You can resize the blue container</p>

<div class="parent">
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
</div>

Solution B (3 - 5 columns with exactly 64px width per column)

/* Note the difference with solution A on the grid-template-columns value */

.parent {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(0, min(100%/3, max(64px, 100%/5))));
}

.child {
  width: 64px;
  height: 64px;
  max-width: 100%;
}

.parent {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(0, min(100%/3, max(64px, 100%/5))));
}

.child {
  width: 64px;
  height: 64px;
  max-width: 100%;
}


/* Styles not related to the goal */
html {
  box-sizing: border-box;
}

*,
*:before,
*:after {
  box-sizing: inherit;
}

body {
  margin-top: 0;
  text-align: center;
}

.parent {
  justify-content: space-evenly;
  margin: 0 auto;
  padding: 5px;
  border: 5px solid dodgerblue;
  background: wheat;
  overflow: auto;
  resize: both;
}

.child {
  margin: 0 auto;
  border: 2px solid brown;
}
<p>You can resize the blue container</p>

<div class="parent">
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
</div>

Reusable solutions (Sass mixin)

If you want a reusable function to easily generate a column layout with diferent maximum and minimum column values, allowing you to add some gap between columns or adjusting the minimum width for the columns, you should take advantage of Sass mixins and CSS variables like this:

/* For solution A - recommended */
@mixin grid-min-max-cols($min-cols, $max-cols, $cols-min-width, $grid-row-gap: 0px, $grid-column-gap: 0px) {
  --min-cols: #{$min-cols};
  --max-cols: #{$max-cols};
  --cols-min-width: #{$cols-min-width};
  --grid-row-gap: #{$grid-row-gap};
  --grid-column-gap: #{$grid-column-gap};
  
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min((100%/var(--min-cols) - var(--grid-column-gap)*(var(--min-cols) - 1)/var(--min-cols)), max(var(--cols-min-width), (100%/var(--max-cols) - var(--grid-column-gap)*(var(--max-cols) - 1)/var(--max-cols)))), 1fr));
  gap: var(--grid-row-gap) var(--grid-column-gap);
}

/* For solution B */
@mixin grid-min-max-cols($min-cols, $max-cols, $cols-min-width, $grid-row-gap: 0px, $grid-column-gap: 0px) {
  --min-cols: #{$min-cols};
  --max-cols: #{$max-cols};
  --cols-min-width: #{$cols-min-width};
  --grid-row-gap: #{$grid-row-gap};
  --grid-column-gap: #{$grid-column-gap};
  
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(0, min((100%/var(--min-cols) - var(--grid-column-gap)*(var(--min-cols) - 1)/var(--min-cols)), max(var(--cols-min-width), (100%/var(--max-cols) - var(--grid-column-gap)*(var(--max-cols) - 1)/var(--max-cols))))));
  gap: var(--grid-row-gap) var(--grid-column-gap);
}

.parent {
  @include grid-min-max-cols(3, 5, 64px, 5px, 5px);
}

Check my CodePen to see a fully functional example of both reusable solutions (A and B):

https://codepen.io/btous/pen/QWvGNGm

Similar approach with flexbox

Check my CodePen to see a fully functional example of a similar approach with flexbox:

https://codepen.io/btous/pen/OJzwdJw

LETS GO DEEPER!

Understanding the minmax() function within the grid-template-columns CSS property

The CSS grid-template-columns: repeat(auto-fit, minmax(min, max)) statement, tells the browser to calculate the number of columns for an element container based on its own width and the min and max values for the minmax() function. So the key to solve this problem is to understand how the minmax(min, max) function works inside the repeat() function, on the grid-template-columns CSS property.

  1. If we have a grid container with the following CSS property and value, grid-template-columns: repeat(auto-fit, minmax(min, max), it takes the container and divides it with as many columns as can fit with the width of the max parameter for minmax(). So if we set the grid-template-columns property to repeat(auto-fit, minmax(50px, 100px) on a 500px container, it will divide it into 5 columns of 100px since this is the max parameter for the minmax() function.
  2. In case the container increases its width to 550px it still can fit up to 5 columns (since to fit another 100px column it should be at least 600px wide), but it will have a 50px remaining space that will be left empty.
  3. If the second value of minmax() function is smaller than the first one, it takes the first value as its final value, so minmax(100px, 50px) will result into 100px.
  4. If the max value is set to 1fr (1 fraction), the browser will behaves as explained in the first and second points but taking the min value to calculate the number of the columns to divide the container and, if there is any remaining space, it will distributed equally between all the columns. So if we set the grid-template-columns property to repeat(auto-fit, minmax(100px, 1fr) on a container with the same width of the container defined on the second point (550px) it still will fit up to 5 columns and will have a 50px remaining space, but this remaining space won't be left empty, instead it will be distributed equally with all the 5 columns (20px per column) resulting on a 5 columns layout of 120px each.

See this CSS-TRICKS post for more details:

https://css-tricks.com/auto-sizing-columns-css-grid-auto-fill-vs-auto-fit

Now that we know how it works, we can face the problem and solve it step by step to set the min and max parameters for the minmax() function.

We have 2 different approaches to let the grid-template-columns property calculates the number of columns:

  • The first one, as explained on the fourth point, is to set the min value to the width we want for each column and the max value to 1fr

    minmax(?px, 1fr)
    
  • The second one, as explained on the first two points, is to set the max value to the width we want for each column and the min value to 0px since we want to ensure that the min parameter is never higher than the max one to avoid it will be taken as the final value as explained on the third point

    minmax(0px, ?px)
    

The point here is that if the value set for the ? sign is static, when the container can fit more than the maximum number of columns desired, it will keep adding them to the layout and when there is no available space in the container to fit the minimum number of columns desired, it will keep removing them from the layout.

Ok then, now we know that we need to let the browser calculates this value dynamically, to ensure it never breaks our maximum and minimum columns layout defined, so let's do it!

Calculate the columns width dynamically for the minmax() function

This is the trickiest part. First of all we need that the number of columns never exceeds the maximum number of columns we desire (in your case it's 5, but could be any value).

To accomplish that, we have to ensure that the columns width never exceeds the container width divided by the maximum number of columns (5 for you), i.e. 100%/5, so for now we have the following:

/* For solution A - recommended (note the difference with solution B on the grid-template-columns value) */
.parent {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(calc(100%/5), 1fr));
}

/* For solution B (note the difference with solution A on the grid-template-columns value) */
.parent {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(0, calc(100%/5)));
}

Cool, now the layout doesn't exceed the maximum of 5 columns, but it always returns a 5 columns layout since the container will always could fit 5 columns of its same width divided by 5. See snippets below:

Solution A:

.parent {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(calc(100%/5), 1fr));
}

.child {
  width: 64px;
  height: 64px;
  max-width: 100%;
}


/* Styles not related to the goal */
html {
  box-sizing: border-box;
}

*,
*:before,
*:after {
  box-sizing: inherit;
}

body {
  margin-top: 0;
  text-align: center;
}

.parent {
  justify-content: space-evenly;
  margin: 0 auto;
  padding: 5px;
  border: 5px solid dodgerblue;
  background: wheat;
  overflow: auto;
  resize: both;
}

.child {
  margin: 0 auto;
  border: 2px solid brown;
}
<p>You can resize the blue container</p>

<div class="parent">
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
</div>

Solution B:

.parent {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(0, calc(100%/5)));
}

.child {
  width: 64px;
  height: 64px;
  max-width: 100%;
}


/* Styles not related to the goal */
html {
  box-sizing: border-box;
}

*,
*:before,
*:after {
  box-sizing: inherit;
}

body {
  margin-top: 0;
  text-align: center;
}

.parent {
  justify-content: space-evenly;
  margin: 0 auto;
  padding: 5px;
  border: 5px solid dodgerblue;
  background: wheat;
  overflow: auto;
  resize: both;
}

.child {
  margin: 0 auto;
  border: 2px solid brown;
}
<p>You can resize the blue container</p>

<div class="parent">
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
</div>

To make layout reduce columns when it reduces its width we have to tell him the minimum column width you want, i.e. 64px, so it can start removing columns whenever them don't fit in the container width.

We'll do that by wrapping the parameter of the minmax() function inside a max() function that will take the maximum value between our 2 parameters (the desired minimum column width, i.e. 64px, and the one we already have, i.e. calc(100%/5)), giving us the following:

/* For solution A - recommended (note the difference with solution B on the grid-template-columns value) */
.parent {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(max(64px, 100%/5), 1fr));
}

/* For solution B (note the difference with solution A on the grid-template-columns value) */
.parent {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(0, max(64px, 100%/5)));
}

Note that we don't need the calc() function to operate inside the max().

With the code above, whenever the result of 100%/5 is less than 64px it will set the max() function value to 64px and the minmax() function will be converted to minmax(64px, 1fr) (for solution A) or minmax(0, 64px) (for solution B) and will divide the container width by 64px to calculate how many columns can fit. See snippets below:

Solution A:

.parent {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(max(64px, 100%/5), 1fr));
}

.child {
  width: 64px;
  height: 64px;
  max-width: 100%;
}


/* Styles not related to the goal */
html {
  box-sizing: border-box;
}

*,
*:before,
*:after {
  box-sizing: inherit;
}

body {
  margin-top: 0;
  text-align: center;
}

.parent {
  justify-content: space-evenly;
  margin: 0 auto;
  padding: 5px;
  border: 5px solid dodgerblue;
  background: wheat;
  overflow: auto;
  resize: both;
}

.child {
  margin: 0 auto;
  border: 2px solid brown;
}
<p>You can resize the blue container</p>

<div class="parent">
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
</div>

Solution B:

.parent {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(0, max(64px, 100%/5)));
}

.child {
  width: 64px;
  height: 64px;
  max-width: 100%;
}


/* Styles not related to the goal */
html {
  box-sizing: border-box;
}

*,
*:before,
*:after {
  box-sizing: inherit;
}

body {
  margin-top: 0;
  text-align: center;
}

.parent {
  justify-content: space-evenly;
  margin: 0 auto;
  padding: 5px;
  border: 5px solid dodgerblue;
  background: wheat;
  overflow: auto;
  resize: both;
}

.child {
  margin: 0 auto;
  border: 2px solid brown;
}
<p>You can resize the blue container</p>

<div class="parent">
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
</div>

Now we have properly set the maximum number of columns but still need to set the minimum (3 in your case). For that we need to ensure that the parameter for minmax() will never be higher than a third part of the container, i.e. 100%/3.

We will do that by wrapping again the parameter of the minmax() function, but in this case inside a min() function, and besides the value we already have, i.e. max(64px, 100%/5), we will add the value found in the previous paragraph, resulting in the following:

/* For solution A - recommended (note the difference with solution B on the grid-template-columns value) */
.parent {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%/3, max(64px, 100%/5)), 1fr));
}

/* For solution B (note the difference with solution A on the grid-template-columns value) */
.parent {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(0, min(100%/3, max(64px, 100%/5))));
}

You can see their behavior on the snippets below:

Solution A:

.parent {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%/3, max(64px, 100%/5)), 1fr));
}

.child {
  width: 64px;
  height: 64px;
  max-width: 100%;
}


/* Styles not related to the goal */
html {
  box-sizing: border-box;
}

*,
*:before,
*:after {
  box-sizing: inherit;
}

body {
  margin-top: 0;
  text-align: center;
}

.parent {
  justify-content: space-evenly;
  margin: 0 auto;
  padding: 5px;
  border: 5px solid dodgerblue;
  background: wheat;
  overflow: auto;
  resize: both;
}

.child {
  margin: 0 auto;
  border: 2px solid brown;
}
<p>You can resize the blue container</p>

<div class="parent">
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
</div>

Solution B:

.parent {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(0, min(100%/3, max(64px, 100%/5))));
}

.child {
  width: 64px;
  height: 64px;
  max-width: 100%;
}


/* Styles not related to the goal */
html {
  box-sizing: border-box;
}

*,
*:before,
*:after {
  box-sizing: inherit;
}

body {
  margin-top: 0;
  text-align: center;
}

.parent {
  justify-content: space-evenly;
  margin: 0 auto;
  padding: 5px;
  border: 5px solid dodgerblue;
  background: wheat;
  overflow: auto;
  resize: both;
}

.child {
  margin: 0 auto;
  border: 2px solid brown;
}
<p>You can resize the blue container</p>

<div class="parent">
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
</div>

Allowing a CSS column gap

With that, we already have set up your layout, but it will be nice to set a gap to our grid without breaking it, so lets do it!

If we want to set a column-gap property (since the row gap won't affect our layout) to, lets say, 5px, when we calculate the columns width to set the layout we have to take this property into account. If not, it will break.

To do that we have to subtract the gap for each column from the operations to calculate the min and max parameters for the minmax() function.

We can get the proportional column gap for each column multiplying the column-gap value by the number of columns minus one, since there is always one less gap than columns. That will result in:

/* For solution A - recommended (note the difference with solution B on the grid-template-columns value) */
.parent {
  display: grid;
  row-gap: 5px;
  column-gap: 5px;
  grid-template-columns: repeat(auto-fit, minmax(min(100%/3 - 5px*(3-1)/3, max(64px, 100%/5 - 5px*(5-1)/5)), 1fr));
}

/* For solution B (note the difference with solution A on the grid-template-columns value) */
.parent {
  display: grid;
  row-gap: 5px;
  column-gap: 5px;
  grid-template-columns: repeat(auto-fit, minmax(0, min(100%/3 - 5px*(3-1)/3, max(64px, 100%/5 - 5px*(5-1)/5))));
}

See their behavior on the snippets below:

Solution A:

.parent {
  display: grid;
  row-gap: 5px;
  column-gap: 5px;
  grid-template-columns: repeat(auto-fit, minmax(min(100%/3 - 5px*(3 - 1)/3, max(64px, 100%/5 - 5px*(5 - 1)/5)), 1fr));
}

.child {
  width: 64px;
  height: 64px;
  max-width: 100%;
}


/* Styles not related to the goal */
html {
  box-sizing: border-box;
}

*,
*:before,
*:after {
  box-sizing: inherit;
}

body {
  margin-top: 0;
  text-align: center;
}

.parent {
  justify-content: space-evenly;
  margin: 0 auto;
  padding: 5px;
  border: 5px solid dodgerblue;
  background: wheat;
  overflow: auto;
  resize: both;
}

.child {
  margin: 0 auto;
  border: 2px solid brown;
}
<p>You can resize the blue container</p>

<div class="parent">
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
</div>

Solution B:

.parent {
  display: grid;
  row-gap: 5px;
  column-gap: 5px;
  grid-template-columns: repeat(auto-fit, minmax(0, min(100%/3 - 5px*(3 - 1)/3, max(64px, 100%/5 - 5px*(5 - 1)/5))));
}

.child {
  width: 64px;
  height: 64px;
  max-width: 100%;
}


/* Styles not related to the goal */
html {
  box-sizing: border-box;
}

*,
*:before,
*:after {
  box-sizing: inherit;
}

body {
  margin-top: 0;
  text-align: center;
}

.parent {
  justify-content: space-evenly;
  margin: 0 auto;
  padding: 5px;
  border: 5px solid dodgerblue;
  background: wheat;
  overflow: auto;
  resize: both;
}

.child {
  margin: 0 auto;
  border: 2px solid brown;
}
<p>You can resize the blue container</p>

<div class="parent">
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
</div>

Making a reusable function for any number of minimum and maximum columns, any column minimum width and any gap value

We have already achieved our goal of making a responsive layout of a maximum of 5 and a minimum of 3 columns, a minimum width for each column of 64px and a gap of 5px between the columns. This looks really nice, but what if for some reason we want to change one of this properties value or some of them? We will have to review the grid-template-columns property and after remembering and understanding all the functions and numbers, change it to what we need.

Why not taking advantage of Sass mixins?

We can create a mixin that takes min-cols, max-cols, cols-min-width, grid-row-gap and grid-column-gap as its parameters and replace the explicit values in the grid-template-columns and gap properties with them. Then in the container selector on the CSS file we can include this mixin and let it do the job for us.

In SCSS it will look like this:

/* For solution A - recommended (note the difference with solution B on the grid-template-columns value) */
@mixin grid-min-max-cols($min-cols, $max-cols, $cols-min-width, $grid-row-gap: 0px, $grid-column-gap: 0px) {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min((100%/$min-cols - $grid-column-gap*($min-cols - 1)/$min-cols), max($cols-min-width, (100%/$max-cols - $grid-column-gap*($max-cols - 1)/$max-cols))), 1fr));
  gap: $grid-row-gap $grid-column-gap;
}

/* For solution B (note the difference with solution A on the grid-template-columns value) */
@mixin grid-min-max-cols($min-cols, $max-cols, $cols-min-width, $grid-row-gap: 0px, $grid-column-gap: 0px) {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(0, min((100%/$min-cols - $grid-column-gap*($min-cols - 1)/$min-cols), max($cols-min-width, (100%/$max-cols - $grid-column-gap*($max-cols - 1)/$max-cols)))));
  gap: $grid-row-gap $grid-column-gap;
}

.parent {
  @include grid-min-max-cols(3, 5, 64px, 5px, 5px);
}

Allow modifying the layout values at runtime

Finally, to allow modifying any of the values at runtime, we can define a CSS variable for each mixin parameter and use them to replace the Sass variables in the grid-template-columns and gap properties. With this we will be able to easily redefine any of the CSS variables at runtime and the layout will immediately adapt to it.

In SCSS it will look like this:

/* For solution A - recommended (note the difference with solution B on the grid-template-columns value) */
@mixin grid-min-max-cols($min-cols, $max-cols, $cols-min-width, $grid-row-gap: 0px, $grid-column-gap: 0px) {
  --min-cols: #{$min-cols};
  --max-cols: #{$max-cols};
  --cols-min-width: #{$cols-min-width};
  --grid-row-gap: #{$grid-row-gap};
  --grid-column-gap: #{$grid-column-gap};
  
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min((100%/var(--min-cols) - var(--grid-column-gap)*(var(--min-cols) - 1)/var(--min-cols)), max(var(--cols-min-width), (100%/var(--max-cols) - var(--grid-column-gap)*(var(--max-cols) - 1)/var(--max-cols)))), 1fr));
  gap: var(--grid-row-gap) var(--grid-column-gap);
}

/* For solution B (note the difference with solution A on the grid-template-columns value) */
@mixin grid-min-max-cols($min-cols, $max-cols, $cols-min-width, $grid-row-gap: 0px, $grid-column-gap: 0px) {
  --min-cols: #{$min-cols};
  --max-cols: #{$max-cols};
  --cols-min-width: #{$cols-min-width};
  --grid-row-gap: #{$grid-row-gap};
  --grid-column-gap: #{$grid-column-gap};
  
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(0, min((100%/var(--min-cols) - var(--grid-column-gap)*(var(--min-cols) - 1)/var(--min-cols)), max(var(--cols-min-width), (100%/var(--max-cols) - var(--grid-column-gap)*(var(--max-cols) - 1)/var(--max-cols))))));
  gap: var(--grid-row-gap) var(--grid-column-gap);
}

.parent {
  @include grid-min-max-cols(3, 5, 64px, 5px, 5px);
}

With that, we have all we need to generate our nice columns layout with a maximum and a minimum columns number.

Check my CodePen to see a fully functional example of both reusable solutions (A and B):

https://codepen.io/btous/pen/QWvGNGm

Encumbrancer answered 12/9, 2021 at 18:32 Comment(3)
To make it work with Firefox and safari, it's necessary tu add calc() inside the calculations of min() and max() functionsRhomb
Hi @DanielaWitteveen, sorry for the late response. I have tested it in Safari 14 and above (iOS), Safari 13.1 and above (Mac), Firefox in Windows and Mac... and it is always working fine in all browsers. Are you sure you are having problems with this? Could you give me more info about your problem? Thanks!Encumbrancer
deserves a chapter in a bookHelprin
N
23

you can get required output by using @media query used grid-template-columns: repeat(5, 1fr); for large device and grid-template-columns: repeat(3, 1fr); for small device

body {
  background-color: wheat;
}

.parent {
  display: grid;
  grid-gap: 10px;
  grid-template-columns: repeat(5, 1fr);  
}

.child {
  height: 64px;
  width: 64px;
  background-color: brown;
}

@media(max-width:540px){
  .parent {
     grid-template-columns: repeat(3, 1fr);
  }
}
<body>
  <div class="parent">
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
  </div>
</body>
Nabors answered 20/9, 2018 at 5:45 Comment(2)
Thanks for the answer but is it possible to do this without using media queries or flexbox? Sorry, I added this condition after posting the question. I have mentioned it at the end of the question.Perspicuity
It might be possible and browser support will be limited. media query will provide the freedom that you can make any kind of layout based on regulation.. so need to approach better optionNabors
A
10

I think this is you are looking for.

body {
  background-color: wheat;
}

.parent {
  display: grid;
  grid-gap: 2vw;
  grid-template-columns: repeat(auto-fill, minmax(100px, 120px));
  max-width: 800px;
}

.child {
  height: 64px;
  width: 64px;
  background-color: brown;
}
<body>
  <div class="parent">
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
    <div class="child"></div>
  </div>
</body>

I have changed - grid-template-columns: repeat(auto-fill, minmax(100px, 140px)); to grid-template-columns: repeat(auto-fill, minmax(100px, 120px));

Because in the small screen size the child will take 140px area reserved resulting it to break to 2 columns in small screen now I have changed it to 120px and grid-gap: 2vw; to solve this issue.

Avertin answered 20/9, 2018 at 5:50 Comment(0)
P
5

https://codepen.io/pandiyancool/pen/oPmgeP

css

body {
  background-color: wheat;
}

.parent {
  display: flex;
  flex-wrap: wrap;
}

.child {
  height: 80px;
  padding: 25px;
  background-color: brown;
  border: 1px solid #ccc;
  flex: 1 1 160px;
  flex-basis: 20%;
}
Planchette answered 20/9, 2018 at 5:35 Comment(1)
Thanks for the answer but is it possible to do this without using media queries or flexbox? Sorry, I should have included this in the question. Edited now.Perspicuity
P
2

Here is an easy way to set the maximum number of columns of a grid to 3. It's not a very elegant solution but it's easy to read and understand.

If you happen to have less than 3 children, the gird will fill the empty space.

.grid {
  display: grid;
  gap: 10px;

  &:has(> :nth-child(2)) {
    grid-template-columns: repeat(2, 1fr);
  }

  &:has(> :nth-child(3)) {
    grid-template-columns: repeat(3, 1fr);
  }
}

.grid-item {
  min-height: 100px;
  border-radius: 6px;
  border: 1px solid gray;
}
<h5>grid example 1:</h5>
<div class="grid">
  <div class="grid-item">
    <div><!-- nested child 1 --></div>
    <div><!-- nested child 2 --></div>
    <div><!-- nested child 3 --></div>
  </div>
  <div class="grid-item"></div>
</div>

<h5>grid example 2:</h5>
<div class="grid">
  <div class="grid-item"></div>
  <div class="grid-item"></div>
  <div class="grid-item"></div>
  <div class="grid-item"></div>
</div>
Prestidigitation answered 28/4 at 13:33 Comment(0)
P
2

Width grid-template-areas you can get as specific as you like, however, you should use media queries, as defining different layouts based on viewport size is precisely what they're for.

The advantage of this approach is that the CSS allows you to visualize the runtime layout directly in the code while avoiding minimum and maximum arithmetic, other than defining breakpoints. This makes it easier to reason about and maintain in my opinion.

main {
  background: wheat;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  grid-auto-rows: minmax(64px, auto);
  grid-template-areas:
    'd1 d2 d3 d4 d5'
    'd6 d7 d8 d9 d10';
  gap: 10px;
  max-width: 800px;
  margin: 0 auto;
}
div {
  grid-area: d1;
  justify-self: center;
  width: 64px;
  height: 64px;
  background: brown;
}
div#d2 {
  grid-area: d2;
}
div#d3 {
  grid-area: d3;
}
div#d4 {
  grid-area: d4;
}
div#d5 {
  grid-area: d5;
}
div#d6 {
  grid-area: d6;
}
div#d7 {
  grid-area: d7;
}
div#d8 {
  grid-area: d8;
}
div#d9 {
  grid-area: d9;
}
div#d10 {
  grid-area: d10;
}

@media (286px <= width <= 359px) {
  main {
    grid-template-columns: repeat(4, 1fr);
    grid-template-areas:
      'd1 d2 d3 d4'
      'd5 d6 d7 d8'
      'd9 d10 . .';
  }
}

@media (width < 286px) {
  main {
    grid-template-columns: repeat(3, 1fr);
    grid-template-areas:
      'd1 d2 d3'
      'd4 d5 d6'
      'd7 d8 d9'
      'd10 . .';
  }
}
<main>
  <div id="d1"></div>
  <div id="d2"></div>
  <div id="d3"></div>
  <div id="d4"></div>
  <div id="d5"></div>
  <div id="d6"></div>
  <div id="d7"></div>
  <div id="d8"></div>
  <div id="d9"></div>
  <div id="d10"></div>
</main>

The question doesn't really state what the columns are used for, so assuming you want to place content within them while setting a minimum width, you can do this more simply with flex-flow: wrap and still restrict the column count to between 5 and 3.

main {
  background: wheat;
  display: flex;
  flex-flow: wrap;
  gap: 10px;
  max-width: 800px;
  /* 64 * 3 + 2 * 10 (min columns + sum of gap) */
  min-width: 212px;
  margin: 0 auto;
}
div {
  height: 64px;
  min-width: 64px;
  /* 20% - 10px (max columns - gap) */
  flex: 1 0 calc(20% - 10px);
  background: brown;
}
<main>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</main>
Puerility answered 18/5 at 0:50 Comment(0)
B
1

Not sure if you mind fluid columns, but using media queries you could do:

.parent { grid-template-columns: repeat(3, 1fr); } for small screens

and

.parent { grid-template-columns: repeat(5, 1fr); } for larger screens

Bellows answered 20/9, 2018 at 5:37 Comment(2)
Thanks for the answer but is it possible to do this without using media queries or flexbox? Sorry, I should have included this in the question. Edited now.Perspicuity
Probably, and I reckon minmax is the way to go, especially since your divs are fixed width.Bellows
P
1

Add a min-width to the parent element!

.parent {
  display: grid;
  grid-gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(100px, 140px));
  min-width: 440px;
  max-width: 800px;
}

That should do it. Here's the CodePen.

Placard answered 23/5 at 12:38 Comment(0)
P
1
body {
  background-color: wheat;
}

.parent {
  display: grid;
  grid-gap: 10px;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  max-width: 800px;
}

.child {
  height: 64px;
  width: 64px;
  background-color: brown;
}

I think this might work

Prosector answered 24/5 at 4:49 Comment(0)
I
-1

I just found a neat trick that worked for me. I'm using chrome.

grid-template-columns: repeat(7, 1fr);
grid-template-columns: repeat(auto-fit, 1fr);
Iveyivie answered 13/3, 2022 at 18:50 Comment(2)
I love that this received a bounty. Downvoters gonna hate.Puerility
@morganney: me too!Helprin

© 2022 - 2024 — McMap. All rights reserved.