1

My scenario

I have these two flex containers (the difficulty options and the max-score options):

enter image description here

I want the 'easy', 'medium' and 'hard' button to share the same width, but also to fit they're content (in this case, because 'medium' is the longest, they should all equal its width).

I want the same behavior with the bottom buttons (but for them to have a smaller width since they need to accommodate for smaller content).

Right now the flex containers for both of them is set to:

display: flex;
flex-direction: wrap;
flex-wrap: wrap;

And the flex children are each set to their default flex values, with a set height and an auto width.

Approaches I've tried

First approach - flex-basis and flex-grow

Setting the children to flex-basis: 0 and flex-grow: 1, as I've seen in past questions, but then my wrapped child fills the entire width, and the top buttons aren't the same width:

enter image description here

Second approach - -- hardcoded flex-basis

Setting all children to flex-basis: 90px (90px to accommodate for the biggest button, 'medium') which does make them all the same width, but then the width is fixed and doesn't adjust to only fit the content (specifically this is desired so the score buttons can fit in two rows instead of three).

enter image description here

Third approach - max-width

The closest I've got to is to set the children to:

```
max-width: 90px;
flex-basis: 0;
flex-grow: 1; 
```

Which makes them behave as wanted:

enter image description here

But when the screen width shrinks, the buttons start to differ in width (the obvious one is the '200' button bigger than the other scores, but also 'medium' is bigger than 'easy' and 'hard'):

enter image description here

My code:

.flex-col,
.flex-row {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  justify-content: center;
}

.flex-col {
  flex-direction: column;
}

.flex-row {
  flex-direction: row;
}

.button {
  border-style: solid;
  padding: 4px;
}

.parent {
  height: auto;
  width: auto;
}
<div class="parent flex-col">
  <div class="flex-col">
    <div class="flex-row">
      DIFFICULTY
    </div>
    <div class="flex-row">
      <div class="button">EASY</div>
      <div class="button">MEDIUM</div>
      <div class="button">HARD</div>
    </div>
  </div>
  <div class="flex-col">
    <div class="flex-row">
      MAX SCORE
    </div>
    <div class="flex-row">
      <div class="button">50</div>
      <div class="button">75</div>
      <div class="button">100</div>
      <div class="button">150</div>
      <div class="button">200</div>
    </div>
  </div>
</div>

Help appreciated, thanks!

Alon Emanuel
  • 172
  • 3
  • 10
  • 2
    Where is your code? It's ok to use images to illustrate what you are after but you need to provide a [mcve] that allows to reproduce the issue. – MrUpsidown Feb 02 '23 at 15:21
  • Well since you are using a max-width: 90px, you might as well use a min-width: 90px, making them the same size at all times. You can then use flex-wrap: wrap on the parent to make the break line on smaller screens. – Jake Feb 02 '23 at 15:26
  • @Sun how is `min-width: 90px;` going to help when the button is too wide? – MrUpsidown Feb 02 '23 at 15:35
  • You did all you could, flex takes into account line by line (or columns) It is not a 2d grid system, and grid will not center the last element if alone on a line of cells already there. You need a bit of javascript to find the largest one and apply the same to all of them, then flex is fine with flex-basis – G-Cyrillus Feb 02 '23 at 15:36
  • Possible duplicate of [How to keep wrapped flex-items the same width as the elements on the previous row?](https://stackoverflow.com/questions/23274338/how-to-keep-wrapped-flex-items-the-same-width-as-the-elements-on-the-previous-ro). – MrUpsidown Feb 02 '23 at 15:39
  • Possible duplicate of: [CSS Keep all flexbox children elements the same size](https://stackoverflow.com/questions/25514579/css-keep-all-flexbox-children-elements-the-same-size) – Roko C. Buljan Feb 02 '23 at 15:43
  • @Sun Changing it to ```min-width``` won't let the buttons shrink to smaller content, which is what I'm looking for. – Alon Emanuel Feb 02 '23 at 15:51

1 Answers1

1

The closest way to do this with CSS only, is to use a grid instead of a flexbox for reasons well explained here.

The only way to truly do what you are asking (make all children have the same width as the widest child), is with JavaScript. Loop through the elements to find the biggest width and set them all to have the found width.

Here is a snippet demonstrating both concepts:

const equalizers = document.querySelectorAll('.equalize')
let r = 0

equalizers.forEach(equalizer => {

  const widths = []
  for (const btn of equalizer.children) {
    const w = btn.getBoundingClientRect().width
    // Math.ceil() is optional to avoid long floats
    widths.push(Math.ceil(w)) // 82
    // widths.push(w) // 81.31945037841797
  }
  const biggest = Math.max(...widths)
  console.log(`biggest width found in row[${r++}]:`, biggest)

  for (const btn of equalizer.children) {
    btn.style.width = `${biggest}px`
  }

})
.flex-col,
.flex-row {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  justify-content: center;
}

.flex-col {
  flex-direction: column;
}

.flex-row {
  flex-direction: row;
}

.button {
  border-style: solid;
  padding: 4px;
}

.parent {
  height: auto;
  width: auto;
}

.grid-row {
  display: grid;
  gap: 4px;
}

.grid-row>* {
  text-align: center;
}

@media (min-width: 25em) {
  .grid-row {
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
  }
}

.flex-row>* {
  text-align: center;
}
<hr>
<strong>JavaScript</strong> (only ever as wide as the widest sibling, with wrapping)
<hr>
<div class="parent flex-col">
  <div class="flex-col">
    <div class="flex-row">
      DIFFICULTY
    </div>
    <div class="flex-row equalize">
      <div class="button">EASY</div>
      <div class="button">MEDIUM</div>
      <div class="button">HARD</div>
    </div>
  </div>
  <div class="flex-col">
    <div class="flex-row">
      MAX SCORE
    </div>
    <div class="flex-row equalize">
      <div class="button">50</div>
      <div class="button">75</div>
      <div class="button">100</div>
      <div class="button">150</div>
      <div class="button">200</div>
    </div>
  </div>
</div>

<hr>
<strong>Grid</strong> (always as wide as posible and no wrapping, either all stacked, or all inline with breakpoint)
<hr>
<div class="parent flex-col">
  <div class="flex-col">
    <div class="flex-row">
      DIFFICULTY
    </div>
    <div class="grid-row">
      <div class="button">EASY</div>
      <div class="button">MEDIUM</div>
      <div class="button">HARD</div>
    </div>
  </div>
  <div class="flex-col">
    <div class="flex-row">
      MAX SCORE
    </div>
    <div class="grid-row">
      <div class="button">50</div>
      <div class="button">75</div>
      <div class="button">100</div>
      <div class="button">150</div>
      <div class="button">200</div>
    </div>
  </div>
</div>
Arleigh Hix
  • 9,990
  • 1
  • 14
  • 31
  • This really seems to be the closest I can get to my desired outcome, thanks! (Should I accept this as an answer? It isn't the actual answer, but as I see there isn't any solution for this, so...) – Alon Emanuel Feb 03 '23 at 10:30