[csswg-drafts] [css-flexbox] Intrinsic main size algorithm for row flexboxes not web compatible (#8884)

davidsgrogan has just created a new issue for https://github.com/w3c/csswg-drafts:

== [css-flexbox] Intrinsic main size algorithm for row flexboxes not web compatible ==
Blink launched an implementation of the specified intrinsic sizing algorithm on Canary for a few days. It was quite clear that the [algorithm for computing the intrinsic main size for single-line row flexboxes](https://drafts.csswg.org/css-flexbox/#intrinsic-main-sizes) is not web compatible. `min-content` sizes are too big. Not yet sure about `max-content` sizes.

The existing web needs the container's `min-content` size to be `100px` in the case below. But the specified algorithm gives it a width of `300px`[1].

```html
<style>
  .flex {
    display: flex;
    width: min-content; /* [2] */
    border: 2px solid;
  }
  span {
    float:left;
    height: 25px;
    width: 100px;
    background: orange;
  }
</style>

<!-- Container must be 100px wide to be web compatible. New algorithm makes it 300px -->
<div class="flex">
  <!-- flex-basis of this item is 300px. -->
  <!-- min-content contribution is 100px. -->
  <!-- desired flex fraction is negative -->
  <div>
    <span></span>
    <span></span>
    <span></span>
  </div>
  <!-- desired flex fraction is 0 -->
  <div id="some-other-item-where-flex-fraction-is-0"></div>
</div>
```

[1] `chosen flex fraction == 0`. So first item's final contribution is `300px + 0*0`. Second item's [final contribution](https://drafts.csswg.org/css-flexbox/#intrinsic-main-sizes:~:text=Add%20each%20item%E2%80%99s%20flex%20base%20size%20to%20the%20product%20of%20its%20flex%20grow%20factor%20(scaled%20flex%20shrink%20factor%2C%20if%20shrinking)%20and%20the%20chosen%20flex%20fraction%2C%20then%20clamp%20that%20result%20by%20the%20max%20main%20size%20floored%20by%20the%20min%20main%20size) is `0`. Their sum is the min-content width of `300px`.
[2] No one actually specifies `width: min-content`. But the container's `min-content` size percolates up to ancestor flexboxes (used in automatic minimum widths) or table cells (for `fit-content` sizing). The new larger `min-content` sizes cause compat problems for such ancestors.

We implemented a variant of the algorithm that is closer to what engines currently ship. In this variant, for containers where `chosen flex fraction <= 0`, items' final contribution to the container's `min-content` size is (1) its `flex-basis` if the item has a 0 flex factor in whichever direction it wants to go (shrink if `flex-basis > min-content` contribution, grow otherwise); otherwise: (2) its `min-content contribution`.

```javascript
let ComputeIntrinsicSizes = (flex_container) => {
  // https://drafts.csswg.org/css-flexbox/#intrinsic-main-sizes
  let [container_min_size, container_max_size, chosen_flex_fraction] = ComputeIntrinsicMainSizesPerSection991();

  // chosen_flex_fraction <=0 means no item is growing from their flex basis
  // to meet their min contribution.
  if (flex_container.IsSingleLineRow() && chosen_flex_fraction <= 0) {
    container_min_size = 0;

    for (item in flex_container.items) {
      // https://drafts.csswg.org/css-flexbox/#intrinsic-item-contributions
      const min_contribution = item.ComputeMinContributionPerSection993();
      const base_size = item.flex_base_size;
      const cant_move = (item.shrink_factor == 0 && base_size > min_contribution) || 
                        (item.grow_factor == 0 && base_size < min_contribution);
      if (cant_move) {
        container_min_size += base_size;
      } else {
        container_min_size += min_contribution;
      }
    }
  }
  return { container_min_size, container_max_size };
}
```

In local testing, this variant has proven to be significantly more web compatible than what is currently specified, but possibly still insufficiently compatible. We haven't tested it in the wild.

We've also discussed, but haven't experimented much with, another variant even closer to what engines currently ship. In this variant, we don't examine flex fractions at all for `min-content` computations. Instead, each item's final contribution is (1) flex-basis if the `flex-basis` is not derived from the item's contents AND the item has a 0 flex factor in whichever direction it wants to go; otherwise: (2) its `min-content contribution`.

```javascript
let ComputeIntrinsicSizes = (flex_container) => {
  // https://drafts.csswg.org/css-flexbox/#intrinsic-main-sizes
  let [container_min_size, container_max_size, chosen_flex_fraction] = ComputeIntrinsicMainSizesPerSection991();

  if (flex_container.IsSingleLineRow()) {
    container_min_size = 0;

    for (item in flex_container.items) {
      // https://drafts.csswg.org/css-flexbox/#intrinsic-item-contributions
      const min_contribution = item.ComputeMinContributionPerSection993();
      const base_size = item.flex_base_size;
      const cant_move = (item.shrink_factor == 0 && base_size > min_contribution) ||
                        (item.grow_factor == 0 && base_size < min_contribution);
      if (cant_move && item.basis != "content") {
        container_min_size += base_size;
      } else {
        container_min_size += min_contribution;
      }
    }
  }
  return { container_min_size, container_max_size };
}
```

Both of these variants break the promise, in some cases, that an item will end up at least as large as its `min-content contribution` after the flex algorithm runs. But that seems to be necessary given where the web is today. (Also, I omitted clamping by used min/max sizes in the descriptions above, but of course they need to be accounted for.)

@tabatkins @fantasai, you've thought about this algorithm a lot. Do you have ideas for variants that are more principled than our attempts, but that preserve current behavior in the reduced case presented above?

We haven't yet gotten as strong a signal on multiline row or multiline column flexboxes. Investigations ongoing.




Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/8884 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Tuesday, 30 May 2023 22:08:31 UTC