10

I've read a lot of tutorials about CSS flex boxes and CSS grids, but frankly I just can't figure out how to do this simple(?) thing.

All I want is for the elements in a container to all be as wide as the widest element.

I don't want them to fill up the whole width of the container, just all grow to the maximum width. Can that be done with CSS?

Consider this HTML:

<div class="container">
   <button>a normal button</button>
   <button>tiny</button>
   <button>a really, really, really wide button</button>
</div>

That produces something like this:

enter image description here

What I want is something like this, where they're all the width of that widest button:

enter image description here

I DON'T want them to just stretch out to fill the width of the page:

enter image description here

Can this be done? If so, what would be the CSS for the above HTML?

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
tscheingeld
  • 789
  • 7
  • 24
  • 1
    only CSS no. With js yes it's possible. – Rajeev Ranjan Dec 28 '17 at 07:26
  • 1
    Its super simple just using CSS like [this](https://jsfiddle.net/5dt3qq8d/) – J o Dec 28 '17 at 08:56
  • 2
    @Jo He's specifically asking for the items not to stretch to the width of the container. As far as I know, flexbox cannot do this. – zrooda Aug 21 '18 at 11:03
  • 1
    Does this answer your question? [Every item to have the same width as the widest element](https://stackoverflow.com/questions/31159732/every-item-to-have-the-same-width-as-the-widest-element) – leonheess Aug 16 '21 at 18:56

3 Answers3

15

Yes. This can be done with pure CSS.

Here's a simple solution:

.container {
  display: inline-grid;
  grid-template-columns: 1fr 1fr 1fr;
}
<div class="container">
   <button>a normal button</button>
   <button>tiny</button>
   <button>a really, really, really wide button</button>
</div>

Here's how it works:

Grid Layout provides a unit for establishing flexible lengths in a grid container. This is the fr unit. It is designed to distribute free space in the container (and is somewhat analogous to flex-grow).

However, the Grid spec provides for a particularly unique behavior when the container's width / height is dynamic (e.g., display: inline-grid, in this case).

In such cases, the fr unit will compute the max-content of each grid item. It will then take the maximum value it finds and use that as the 1fr length for all items on the track.

That results in grid items that share the width of the widest item in the row.

Strange, but true.

It's also why a layout with equal height rows is possible with Grid (but not flexbox).

Here's the relevant section in the spec:

7.2.3. Flexible Lengths: the fr unit

...

When the available space is infinite (which happens when the grid container’s width or height is indefinite), flex-sized (fr) grid tracks are sized to their contents while retaining their respective proportions.

The used size of each flex-sized grid track is computed by determining the max-content size of each flex-sized grid track and dividing that size by the respective flex factor to determine a “hypothetical 1fr size”.

The maximum of those is used as the resolved 1fr length (the flex fraction), which is then multiplied by each grid track’s flex factor to determine its final size.

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
  • 1
    Works really well and is supported in all major browsers. I'm curious whether there is a way for this to work dynamically? As in, without knowing how many columns there are. – fgblomqvist Oct 26 '18 at 18:33
  • Good solution using `inline-grid` the downside for me here is that you have to know the number of buttons upfront. – hitautodestruct Jul 03 '22 at 09:05
7

Here is a solution using CSS Grid working with any number of buttons:

div.container {
  display: inline-grid;
  grid-auto-columns: 1fr;
}

div.container button {
  grid-row: 1;
}
Vadim Ovchinnikov
  • 13,327
  • 5
  • 62
  • 90
Simon Hi
  • 2,838
  • 1
  • 17
  • 17
  • Why does `position: absolute` on `.container` break this mechanism? – zrooda Aug 21 '18 at 11:59
  • because `position: absolute` does not make any sens to for `inline` elements. See this answers for a longer explanation: https://stackoverflow.com/a/5042666/1510070 – c0l3 Feb 04 '20 at 13:50
0

To solve this for any number of elements (without needing to know the number in advance):

  • Use an inline grid container (display: inline-grid).
  • Set grid-auto-columns to 1fr so that implicitly created column tracks all have a size of 1fr, which makes them all the size of the largest column in this case.
  • Set grid-auto-flow to column, so that new columns (rather than rows) are created when elements are added to the grid.

document.querySelector('#addBtn').addEventListener('click', e => 
  document.querySelector('.container').append(
    Object.assign(document.createElement('button'), 
      {textContent: document.querySelector('input').value})));
.container {
  display: inline-grid;
  grid-auto-columns: 1fr;
  grid-auto-flow: column;
}
<div class="container">
   <button>a normal button</button>
   <button>tiny</button>
   <button>a really, really, really wide button</button>
</div>
<div style="margin-top: 10px;">
  <input placeholder="Button text" />
  <button id="addBtn">Add New Button</button>
</div>
Unmitigated
  • 76,500
  • 11
  • 62
  • 80