tape2
tape2

Reputation: 255

CSS responsive grid layout: grid-column span breaking minmax

I have a neat responsive grid like this:

grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));

This makes each grid item at least 200px wide. One of the items (the red one) is twice as wide, like this:

grid-column-end: span 2;

Great so far!

grid works fine on large screens

Now when I resize to a small width, the red item forces the grid to two columns. This gives me a weird small column, even though I specified the min width at 200px. Look:

grid breaks at small screens

I would really like the red item to collapse to a single column, so that the grid won't break.

The problem however is, I don't know the width of the total grid, so I can't use a mediaquery that triggers at a specific viewport size.

Is there a way to either force the red item to one column, or maybe to define the grid columns in another way that will solve this problem?

Upvotes: 10

Views: 3958

Answers (2)

Quantra
Quantra

Reputation: 31

Inspired by CSS Grid Masonry in the sense that CSS grid nearly does what we want and we can help it with a little JS I have the following solution:

We choose the maximum number of columns in our grid and the minimum width for a column. We have any number of grid items which can span between 1 and our maximum number of columns.

This example uses a maximum of 4 columns with a minimum width of 256px.

HTML:

<div class="grid">
  <div class="w1"></div>
  <div class="w1"></div>
  <div class="w2"></div>
  <div class="w1"></div>
  <div class="w3"></div>
  <div class="w4"></div>
  ...
</div>

CSS:

.grid {
  display: grid;
}

.w1 {
  grid-column-end: span 1;
}
.w2 {
  grid-column-end: span 2;
}
.w3 {
  grid-column-end: span 3;
}
.w4 {
  grid-column-end: span 4;

JS:

// The minimum width in pixels for our columns
const colWidth = 256;
// The maximum number of columns in our grid
const maxCols = 4;

function resizeGrid() {
  const grid = document.getElementsByClassName("grid")[0];
  
  // Calculate the number of cols we can have by dividing the grid width by our colWidth.
  // This is the maximum number of cols we can have if they are all colWidth.
  const gridWidth = grid.getBoundingClientRect().width;
  let cols = Math.floor(gridWidth / colWidth);

  // Clamp this to our maxCols.
  cols = Math.min(maxCols, cols);

  // Set the number of cols in the grid
  grid.style.gridTemplateColumns = "repeat(" + cols + ", 1fr)";

  // Update grid-column spans limiting them to the number of cols we have.
  // We must do this as grid with an item that spans n columns will automatically have a default of n columns.
  for (let j = 1; j < maxCols + 1; j++) {
    for (const gridItem of grid.getElementsByClassName("w" + j)) {
      gridItem.style.gridColumnEnd = "span " + Math.min(j, cols);
    }
  }
}

window.addEventListener("resize", resizeGrid);
resizeGrid();

This works by calculating the number of columns which will fit into our grid given the minimum width. We then set our grid to have that number of columns and we limit our items to span up to the number of columns. This has the effect of collapsing the number of columns based on the width of the grid.

You can see this working in this Codepen.

Upvotes: 0

Gulshan Zaman
Gulshan Zaman

Reputation: 21

You need to keep the dimensions and a little bit of math in mind while using grid. If you're telling an item to span to 2 columns then that means you always want at least 2 columns in your Grid, it'll not turn into a 1 column grid if the screen gets smaller. Now you have a min-width of 400px for your grid.

For that weird sized column, as you have 2 columns and column 2 takes it's min 200px width and the item after that is left with that weird size.

You can write a media query at that breakpoint to tell your item 1 to stay in column 1 and that should fix it.

@media screen and (max-width:415px){
.item1 {
grid-column: 1;
background:red;
}
}

Check out the Codepen I'm linking here

see on Codepen

Upvotes: 2

Related Questions