[csswg-drafts] Define the intrinsic sizing algorithm for block containers (#9120)

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

== Define the intrinsic sizing algorithm for block containers ==
https://drafts.csswg.org/css2/#float-width

> Calculation of the shrink-to-fit width is similar to calculating the width of a table cell using the automatic table layout algorithm. Roughly: calculate the preferred width by formatting the content without breaking lines other than where explicit line breaks occur, and also calculate the preferred minimum width, e.g., by trying all possible line breaks. **CSS 2 does not define the exact algorithm**.

This is especially tricky in the presence of floats, clearance, and children that establish an independent formatting context.

However, we do seem to have good interoperability (at least in the block-level children case), even if there is no WPT coverage. So I would like to discuss resolving on this interoperable behavior.

Some interesting examples:

<details><summary>The max-content size may be bigger than necessary</summary>

```html
<article style="display: inline-block; border: solid">
  <div><!-- Just to prevent the parent from establishing an IFC, otherwise WebKit is different --></div>
  <div style="float: left; width: 50px; height: 30px; background: cyan"></div>
  <div style="float: right; width: 30px; height: 30px; background: yellow"></div>
  <div style="float: left; clear: left; width: 70px; height: 20px; background: magenta"></div>
</article>
```

![](https://github.com/w3c/csswg-drafts/assets/7477678/8cd27507-40e5-4a48-860b-e3529c40bf9a)

It uses a width of 100px, even though 80px would suffice to avoid "breaking lines" between the cyan and the yellow floats.

</details>

<details><summary>The max-content size may not be big enough to avoid "breaking lines"</summary>

```html
<article style="display: inline-block; border: solid; vertical-align: top">
  <div style="float: left; width: 30px; height: 30px; background: cyan"></div>
  <div style="float: left; width: 30px; height: 30px; background: yellow"></div>
  <div style="overflow: hidden; width: 50px; height: 30px; background: magenta"></div>
</article>
<article style="display: inline-block; border: solid; vertical-align: top">
  <div style="float: left; width: 30px; height: 30px; background: cyan"></div>
  <div style="float: left; width: 30px; height: 30px; background: yellow"></div>
  <div>
    <div style="overflow: hidden; width: 50px; height: 30px; background: magenta"></div>
  </div>
</article>
<article style="display: inline-block; border: solid; vertical-align: top">
  <div style="float: left; width: 30px; height: 30px; background: cyan"></div>
  <div style="float: left; width: 30px; height: 30px; background: yellow"></div>
  <div></div>
  <div style="overflow: hidden; width: 50px; height: 30px; background: magenta"></div>
</article>
```

![](https://github.com/w3c/csswg-drafts/assets/7477678/b9dbb211-fece-4f17-af07-073ad6784e03)

Note that the 2nd case just wraps the BFC root inside an intermediate container, and the 3rd case inserts and empty block before it. Both changes reduce the max-content size and then the BFC root no longer fits next to the floats.

</details>

### If the block container contains block-level children <sub><sup>(and is not a table wrapper box?)</sup></sub>:

In Servo I have tried to implement the behavior that I observed on other browsers, I haven't checked their source code but it seems to be:

For the max-content size,
   1. Let `max_size` be 0px. This tracks the maximum size seen so far, not including trailing uncleared floats.
   1. Let `left_floats` be 0px. This tracks the size of the trailing uncleared left floats.
   1. Let `right_floats` be 0px. This tracks the size of the trailing uncleared right floats.
   1. For each block-level child (including floats, ignoring abspos):
       1. Let `clear` be the the computed value of the `clear` CSS property of the child.
       1. If the child continues the same BFC (instead of establishing an independent formatting context),
           1. Set `clear := both` (this is to avoid adding its size to the floats)
       1. If `clear` is different than `none`,
           1. Set `max_size := max(max_size, left_floats + right_floats)`
       1. If `clear` is `left` or `both`,
           1. Set `left_floats := 0px`
       1. If `clear` is `right` or `both`,
           1. Set `right_floats := 0px`
       1. Let `size` be the outer max-content size of the child, floored by 0px
           1. Bug: Firefox doesn't floor when `clear` is none and the element doesn't float??
       1. If the child floats left,
           1. Set `left_floats := left_floats + size`
       1. Else, if the child floats right,
           1. Set `right_floats := right_floats + size`
       1. Else (the child doesn't float),
           1. Set `max_size := max(max_size, left_floats + right_floats + size)`
           1. Set `left_floats := 0px`
           1. Set `right_floats := 0px`
   1. The max-content size is `max(max_size, left_floats + right_floats)`

The min-content size is just the maximum among the outer min-content sizes of each float or in-flow block-level child.

<details><summary>Firefox flooring bug</summary>

```html
<article style="display: inline-block; border: solid; margin-right: 100px">
  <div style="float: right; width: 50px; height: 30px; background: cyan"></div>
  <div style="clear: left; overflow: hidden; width: 100px; height: 30px; background: magenta; margin-right: -500px"></div>
</article>
<article style="display: inline-block; border: solid">
  <div style="float: right; width: 50px; height: 30px; background: cyan"></div>
  <div style="overflow: hidden; width: 100px; height: 30px; background: magenta; margin-right: -500px"></div>
</article>
```

There is no left float, so `clear: left` should have no effect. However, removing it causes Firefox to stop flooring the size of the BFC root by 0px before adding it to the floats, and then the container becomes 0px wide.

| Firefox | Blink/WebKit |
| - | - |
| ![](https://github.com/w3c/csswg-drafts/assets/7477678/71939bdd-5593-4c65-b2b1-f766c11e3be5) | ![](https://github.com/w3c/csswg-drafts/assets/7477678/2e58febc-8fc9-444c-ad5f-4a570bca8aef) |
</details>

### If the block container establishes an inline formatting context:

There is less interoperability here, e.g.

```html
<article style="display: inline-block; border: solid">
  .<!-- Just to force the parent to establish an IFC on Blink -->
  <div style="float: left; width: 50px; height: 30px; background: cyan"></div>
  <div style="float: right; width: 30px; height: 30px; background: yellow"></div>
  <div style="float: left; clear: left; width: 70px; height: 20px; background: magenta"></div>
</article>
```

| Firefox | Blink/WebKit |
| - | - |
| ![](https://github.com/w3c/csswg-drafts/assets/7477678/95ccf190-990e-467a-9012-c5eaa98b2dec) | ![](https://github.com/w3c/csswg-drafts/assets/7477678/0324621d-7f52-4dd8-83d6-e405c7676d0c) |

I haven't implemented this case yet so I don't have the details, but aligning with Firefox seems more consistent with the block-level children case. Blink and WebKit seem to ignore `clear: left`.



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


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

Received on Wednesday, 26 July 2023 22:17:15 UTC