Is it possible to use a fixed min and max width for columns in a CSS grid which are repeating and wrappable?
Asked Answered
C

1

7

I'd like to set up a CSS layout where a container contains a fixed number of flexible width tiles with the following properties:

  • Spacing in between tiles is fixed and consistent
  • They can expand in size between a fixed min and max width, but should all be the same width irrespective of their content. In the example below I'm using 100px min width and 200px max width.
  • They should wrap onto multiple lines if at their minimum width they don't all fit horizontally in the container.
  • If the container is much larger they should not expand larger than their maximum fixed width, should remain centered in the container and the space between them should not expand.

Perhaps it's not possible with pure CSS, but it feels like it should be! There's a minimal example below, which I'll detail here:

https://jsfiddle.net/vztskh6f/1/

Option 1: CSS Grid

This seems to be the best candidate, seeing as we always want tile widths to be consistent. In order to get columns to wrap I believe you need to use the repeat() syntax and repeat(auto-fit, ...) seems best for this use case. The obvious solution seems to be to use repeat(auto-fit, minmax(100px, 200px)). However, this does not work tiles always remain at 200px irrespective of the width of the container. In every other way this is behaving as desired:

.gridContainer {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(100px, 200px));
  padding: 10px;
  gap: 10px;
  justify-items: center;
  justify-content: center;
}

.child {
  width: 100%;
  height: 30px;
  background: #0f0;
  text-align: center;
}

It could be possible to enforce the desired spec with Option 1, by using grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)) and then setting a computed max-width on the container and forcing it to center too, but it feels like this should be possible with CSS Grid alone?

Option 2: Flexbox

Wrapping is much simpler to achieve with flexbox and using the flex on a child means we can easily set flexible tiles with min and max widths. However once a child wraps onto a second line, it will obviously expand to its max width and will be inconsistent with other tiles:

.flexContainer {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
}

.child {
  height: 30px;
  background: #0f0;
  text-align: center;
  min-width: 100px;
  max-width: 200px;
  // this seems to create consistent widths better than flex: 1 1 100px;
  flex: 1 1 0;
  // could use gap if it wasn't for Safari
  margin: 0 5px 10px;
}

Screenshots

The first row is the grid layout, the second is the flex layout, you can see these are behaving nicely at full width, not expanding beyond their max widths with consistent spacing between them:

Flex and Grid layouts full width

As the container shrinks you can see that the grid layout items are not shrinking with it, but immediately wrap. flex layout behaves nicely:

Flex and Grid layouts container shrinking

As the container continues to shrink, the flex starts to wrap in the desired place, but immediately the tile on the next row expands to its max width:

Flex and Grid layouts container small

Complaint answered 7/4, 2022 at 14:49 Comment(0)
C
7

In order to achieve what you're trying to do, you'll need to work on both the container and the child.

On the container, you'll set a min-width to 100px and a max that should be the size of the child:

.gridContainer {
  grid-template-columns: repeat(auto-fit, minmax(100px, max-content));
}

And on each child, you'll have a fixed width, but a max-width of 100% so it shrinks if needed:

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

Updated fiddle: https://jsfiddle.net/7exd5hom/

Counteroffensive answered 7/4, 2022 at 15:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.