Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do percentage-width-sized flex box items not properly increase the width of a wrapping table that has an unspecified (i.e. auto) width?

Tags:

html

css

flexbox

Please take a look at this snippet:

table {
  background-color: yellow;
  box-shadow: inset 0 0 1px red;
  max-width: 100%;
  
  /* You would get similarly strange behavior with collapsing borders: */
  /* border-collapse: collapse; */
}

td {
  background-color: lightgreen;
  box-shadow: inset 0 0 1px blue;
  max-width: 100%;
}

.flex {
  max-width: 100%;
  display: flex;
  flex: 1;
  background-color: lightblue;
  box-shadow: inset 0 0 1px black;
  flex-wrap: wrap;
}

.box {
  width: 50%;
  background-color: cyan;
  border-radius: 9px;
  box-shadow: inset 0 0 1px red;
}
<table>
  <tr>
    <td>
      <div class="flex">
        <div class="box">
          123456789ABCDEFGHIJKL
        </div>
        <div class="box">
          meeedium
        </div>
        <div class="box">
          short
        </div>
      </div>
    </td>
  </tr>
</table>
(Here's a codepen if you want to play with it more easily: http://codepen.io/anon/pen/MKJmBM)

Please note that the text in the upper left box does not completely fit into it. (At least in Chrome it does not.) I was assuming that the wrapping table would try to "push" its width in order for its contents to completely fit into itself. So is this an implementation bug? Or an unspecified behavior? Or is my code simply flawed?

Can someone explain this behavior and provide a solution to fix it?

(By the way... I ran into this strange behavior while experimenting with some initial solution sketches for this question: Using CSS, how to create a two-column grid with equal-width columns that are "only as narrow as required"?)


Edit: The two "visual columns" should both have an equal width. Therefore solutions involving for example max-width: 50%; for .box are not what I'm looking for. My question is really about the table: Why does it not increase its width so that all of its contents fit properly into itself?

A solution for the problem should make sure that the table will not be "too wide": It should only be as wide as required so that the contents will just fit - but not a single subpixel wider.

like image 366
Hauke P. Avatar asked Jan 03 '16 00:01

Hauke P.


2 Answers

This is explained in 9.2. Line Length Determination.

  • First, the flex base size is determined for each item. This case it's complicated, because width: 50% depends on the available size. The spec says

    If the used flex basis is content or depends on its available size, and the flex container is being sized under a min-content or max-content constraint (e.g. when performing automatic table layout [CSS21]), size the item under that constraint. The flex base size is the item’s resulting main size.

    I'm not sure if that means the flex base size will be the min-content or max-content, because CSS tables use both. But since there are no wrap opportunities, both should be the same.

    So the flex base size will be the width of the text.

  • Then,

    Determine the main size of the flex container using the rules of the formatting context in which it participates.

    Then, the table and the flex container will be as wide as the sum of texts.

Once the width of the flex container has been established, the percentages of the flex items can be resolved. But they won't affect the width of the flex container (nor the table).

You can see this better in this snippet:

.flex {
  display: flex;
  background-color: lightblue;
  box-shadow: inset 0 0 1px black;
  flex-wrap: wrap;
}
.box {
  width: 50%;
  background-color: cyan;
  border-radius: 9px;
  box-shadow: inset 0 0 1px red;
}
<table>
  <tr>
    <td>
      <div class="flex">
        <div class="">
          123456789ABCDEFGHIJKL
        </div>
        <div class="">
          meeedium
        </div>
        <div class="">
          short
        </div>
      </div>
    </td>
  </tr>
</table>
<table>
  <tr>
    <td>
      <div class="flex">
        <div class="box">
          123456789ABCDEFGHIJKL
        </div>
        <div class="box">
          meeedium
        </div>
        <div class="box">
          short
        </div>
      </div>
    </td>
  </tr>
</table>

Basically, what you want is make all flex items as wide as the widest one. I don't think there is a way to do that in CSS, but you can use JS.

like image 124
Oriol Avatar answered Sep 22 '22 14:09

Oriol


Actually, while I am not 100% sure about what you want, the solution seems to be close to my previous comment. Remove flex: 1 from .flex and change width:50% in .box to flex-basis: 50%.

Oh, and while you're at it, add a space in the 123456789ABCDEF so it can actually wrap....

table {
  background-color: yellow;
  box-shadow: inset 0 0 1px red;
  max-width: 100%;
  
  /* You would get similarly strange behavior with collapsing borders: */
  /* border-collapse: collapse; */
}

td {
  background-color: lightgreen;
  box-shadow: inset 0 0 1px blue;
  max-width: 100%;
}

.flex {
  max-width: 100%;
  display: flex;
/* flex: 1;  REMOVE */
  background-color: lightblue;
  box-shadow: inset 0 0 1px black;
  flex-wrap: wrap;
}

.box {
  flex-basis: 50%; /* CHANGED from -> width: 50% */
  background-color: cyan;
  border-radius: 9px;
  box-shadow: inset 0 0 1px red;
}
<table>
  <tr>
    <td>
      <div class="flex">
        <div class="box">
          123456789 ABCDEFGHIJKL <!-- NOTICE THE SPACE!!!!-->
        </div>
        <div class="box">
          meeedium
        </div>
        <div class="box">
          short
        </div>
      </div>
    </td>
  </tr>
</table>
like image 44
Rene van der Lende Avatar answered Sep 20 '22 14:09

Rene van der Lende