Sometimes flex-direction: row is better for columns
I was recently working on a project, when I found an interesting situation with CSS where
flex-direction: row is a better fit for a column based arrangement than
In this example, we would like to have this box in the middle of the page horizontally, but also with the width defined by the number of buttons at the bottom, with the title and text box changing size with it.
On the surface, this is a simple problem to solve with flexbox, but alas, it is not.
This is the code you'd typically use for a problem like this:
The rationale is:
- use the flex-direction column layout for the box; a column of 3 rows
- use an inline-flex for the box so it is only as wide as it needs to be to contain the rows
width: 100%on the other columns should define them as the width of the container, and therefore neither can be the widest row in the box
flex-shrink: 0on the buttons row should force the box to treat them as the widest row, as they cannot shrink while the others, with the default value of
However, when set up with this, it ends up looking like this:
However, it seems that the inline-flex box is defining its own width from the width of the whole line of text; why?
This is similar to why setting
height: 100% doesn't do anything when the container's height is
inline-flex is similar to this on the width axis.
That means that the width of the box is still defined as the width of the widest row, which is the text here.
When we hard code the width of the top two rows to the width in pixels of the buttons row, we get this:
Except, of course, it's hardcoded. When we add an extra button, this no longer works:
align-items: stretch is not a perfect cross-axis analogue of
flex-grow: 1, as stretch obeys a fixed size, and flex-grow does not (though it does obey max-width/max-height).
So given that flex-grow can grow beyond the "auto" width; does that mean that it can solve this problem?
It requires an extra wrapper div around the rows that we'd like to be flexible, but indeed it can be done, as shown below:
And the result:
How does it work?
The key with this solution is to use
width: 1px to trick the inline-flex box into thinking that the widest column is the buttons, and then use
flex-grow: 1 (here
flex: 1 0 auto) to grow from auto (i.e. 1px) to fit the space.
To push the button row down, the box needs to be
flex-wrap: wrap and the button row needs to have
width: 100%; which is used here to force it to be on it's own row; though as we discovered before with percentage widths and
display: inline-flex, this doesn't actually change its own width beyond this.
flex-direction: row is better suited than
flex-direction: column for columns.
Go figure... 🤷