10

I am just learning how flexbox works and want to build a very rough prototype to see if flexbox can handle this layout.

I basically want to see if flexbox can support a 4-column table with varying width and height, but with consistent row height.

I'm not sure I am explaining the layout so well, so here is a quick sketch of what I am after:

enter image description here

So what I am concerned with is this: If I was making this using HTML tables, regardless of how much height a particular column has, each row will be consistent.

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
Blankman
  • 259,732
  • 324
  • 769
  • 1,199
  • 1
    What did you try so far? How did that fail to work as you're expecting? – Marko Gresak Jan 29 '18 at 02:25
  • Is this just a learning exercise, or a real project? Cause that looks like (and should be represented as) a table to me... – Ben Hull Jan 29 '18 at 02:38
  • @Beejamin this is for real not learning, but I am learning flex and read about how it will revolutionize the world as we know it so I figured I should use it. – Blankman Jan 29 '18 at 02:45
  • In that case, I'd say the real answer is "use a table and don't worry about flexbox". Semantically, tables are used to mark up content where there is a relationship between all the items that share a row, and all the items that share a column. Going by your sketch, that definitely applies here. – Ben Hull Jan 29 '18 at 05:46
  • 3
    Lots of people claim that flexbox will revolutionize the web as we know it. They're exaggerating. Flexbox is not the universal layout solution people say it is. Flexbox just happens to be the solution that most layouts *should* use because nothing like it existed for 15 years, but it is only *one* such solution. It is not a replacement for tables or inline layout, never has been. (Oddly enough, I'd *just* edited [an answer of mine](https://stackoverflow.com/a/27459133) to say that after you posted this question but before I ever saw it.) – BoltClock Jan 29 '18 at 07:32
  • 1
    As for the title, "Can flexbox handle varying sizes of columns but consistent row height?" the answer is "That's what flexbox is *all* about!" But your sketch suggests that there's more to your question than what the title implies. – BoltClock Jan 29 '18 at 07:39
  • @BoltClock Very well put, and personally I always compare HTML Table with CSS Grid. – Asons Jan 29 '18 at 07:39

2 Answers2

13

I basically want to see if flexbox can support a 4-column table with varying width and height, but with consistent row height.

Simply put, one good feature Flexbox share with HTML Table, is that flex item's on the same row can have the same height, which is controlled by the align-items property. Its default value is stretch, and will by that make items on a row equal high.

.flex-container {
  display: flex;
  flex-wrap: wrap;                    /*  allow items to wrap  */
  justify-content: space-between;     /*  vertical  */
  align-items: stretch;               /*  horizontal, is default
                                          and can be omitted  */
}

.flex-item {
  flex-basis: 23%;                    /*  give each item a width  */
  background-color: #ccc;
  padding: 10px;
  box-sizing: border-box;
}

.flex-item:nth-child(n+5) {
  margin-top: 10px;                   /*  from 5th item, add top margin  */
}
<div class="flex-container">
  <div class="flex-item">Left<br>high</div>
  <div class="flex-item">Middle/Left</div>
  <div class="flex-item">Middle/Right</div>
  <div class="flex-item">Right</div>
  
  <div class="flex-item">Left</div>
  <div class="flex-item">Middle/Left</div>
  <div class="flex-item">Middle/Right</div>
  <div class="flex-item">Right</div>
  
  <div class="flex-item">Left</div>
  <div class="flex-item">Middle/Left</div>
  <div class="flex-item">Middle/Right<br>higher<br>higher</div>
  <div class="flex-item">Right</div>
</div>

One thing that might be worth mentioned, is what Flexbox doesn't share with HTML Table.

  • Dynamic equal column width.

If an item's content will be wider than the rest of the items in the same column, the other items won't adjust their width to equal the widest.

.flex-container {
  display: flex;
  flex-wrap: wrap;                    /*  allow items to wrap  */
  justify-content: space-between;     /*  vertical  */
  align-items: stretch;               /*  horizontal, is default
                                          and can be omitted  */
}

.flex-item {
  flex-basis: 23%;                    /*  give each item a width  */
  background-color: #ccc;
  padding: 10px;
  box-sizing: border-box;
}

.flex-item:nth-child(n+5) {
  margin-top: 10px;                   /*  from 5th item, add top margin  */
}
<div class="flex-container">
  <div class="flex-item">Left<br>high</div>
<div class="flex-item">Middle/Left&nbsp;-&nbsp;&nbsp;is&nbsp;wider</div>
  <div class="flex-item">Middle/Right</div>
  <div class="flex-item">Right</div>
  
  <div class="flex-item">Left</div>
  <div class="flex-item">Middle/Left</div>
  <div class="flex-item">Middle/Right</div>
  <div class="flex-item">Right</div>
  
  <div class="flex-item">Left</div>
  <div class="flex-item">Middle/Left</div>
  <div class="flex-item">Middle/Right<br>higher<br>higher</div>
  <div class="flex-item">Right</div>
</div>

By prevent flex item's to shrink, and enable them to be smaller than their content, it is possible to force equal width.

Note, overflow: hidden (or any value other than visible) has the same effect as min-width: 0 has, so in below sample min-width can be omitted.

.flex-container {
  display: flex;
  flex-wrap: wrap;                    /*  allow items to wrap  */
  justify-content: space-between;     /*  vertical  */
  align-items: stretch;               /*  horizontal, is default
                                          and can be omitted  */
}

.flex-item {
  flex-basis: 23%;                    /*  give each item a width  */
  background-color: #ccc;
  padding: 10px;
  box-sizing: border-box;
  
  flex-shrink: 0;                     /*  prevent from "shrink to fit" */
  min-width: 0;                       /*  allow to shrink past content width  */
  overflow: hidden;                   /*  hide overflowed content  */
}

.flex-item:nth-child(n+5) {
  margin-top: 10px;                   /*  from 5th item, add top margin  */
}
<div class="flex-container">
  <div class="flex-item">Left<br>high</div>
<div class="flex-item">Middle/Left&nbsp;-&nbsp;&nbsp;is&nbsp;wider</div>
  <div class="flex-item">Middle/Right</div>
  <div class="flex-item">Right</div>
  
  <div class="flex-item">Left</div>
  <div class="flex-item">Middle/Left</div>
  <div class="flex-item">Middle/Right</div>
  <div class="flex-item">Right</div>
  
  <div class="flex-item">Left</div>
  <div class="flex-item">Middle/Left</div>
  <div class="flex-item">Middle/Right<br>higher<br>higher</div>
  <div class="flex-item">Right</div>
</div>

A more appropriate comparison would be with CSS Grid, which can do pretty much everything HTML Table can, and then some :)


Here is two great guides to Flexbox and CSS Grid:

Asons
  • 84,923
  • 12
  • 110
  • 165
  • Ironically, you can't implement CSS grid layout in an HTML table, because CSS grid layout expects a grid container element with all its children being grid items, whereas an HTML table is expressed in terms of a table element, followed by table-row elements, followed by table-cell elements per row. So even display: grid isn't a complete replacement for display: table. – BoltClock Jan 29 '18 at 07:47
  • @BoltClock That's why I said _A more appropriate comparison..._ :) – Asons Jan 29 '18 at 07:49
4

Consistent row heights are actually a default setting in flexbox.

You cannot get equal height rows in a flex container, meaning all rows share the same height. For that you could use CSS Grid layout.

But with flexbox each row by itself can be the same height across all columns.

body {
  display: flex;
  justify-content: center;
}

.container {
  display: flex;
  flex-wrap: wrap;
  width: 75%;
}

.box {
  flex: 1 0 20%; /* see note below */
  margin: 0 5px 5px 0;
  background-color: lightgreen;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 1.2em;
}
<div class="container">
  <div class="box">GROUP 1</div>
  <div class="box">text text text text text text text text text text text text text text text text</div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box">GROUP 2</div>
  <div class="box"></div>
  <div class="box">text text text text text text text text</div>
  <div class="box"></div>
  <div class="box">GROUP 3</div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box">text text text text text text text texttext text text text text text text text</div>
  <div class="box">GROUP 4</div>
  <div class="box">text text text text text text text text</div>
  <div class="box"></div>
  <div class="box"></div>
</div>

jsFiddle demo

With regard to flex: 1 0 20%, here's the underlying concept:

  • The flex container is set to wrap.

  • On the flex items, with flex-grow: 1 defined in the flex shorthand, there's no need for flex-basis to be 25%, which would actually result in three items per row due to the margins.

  • Since flex-grow will consume free space on the row, flex-basis only needs to be large enough to enforce a wrap. In this case, with flex-basis: 20%, there's plenty of space for the margins, but not enough space for a fifth item.


More information:

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
  • In your example, how do you guarantee each row has 3 4 columns? I don't see how the "Group x" div forces a newline – Blankman Jan 29 '18 at 13:23
  • 1
    @Blankman In both this and my answer, it is the set `flex-basis`, here 20%. So 4 items, each 20% + margin, won't make space for a 5th within 100%, hence it wraps after 4. I used `flex-basis: 23%`, the long hand property, this answer use the shorthand, `flex: 1 0 20%`, where the last value is `flex-basis`. – Asons Jan 29 '18 at 16:14