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 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].
<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 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
.
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
.
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.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.3