Skip to content

[css-sizing][css-flexbox] It's false that stretch size behaves as stretch alignment #11784

Closed

Description

@Loirooriol

https://drafts.csswg.org/css-sizing-4/#stretch-fit-sizing

We have interoperability against this:

<!DOCTYPE html>
<style>
.container { display: inline-flex; vertical-align: top; width: 100px; height: 100px; border: solid; flex-wrap: wrap; align-content: start }
.stretch { height: -moz-available; height: -webkit-fill-available; height: stretch }
</style>
<div class="container">
  <div class="stretch" style="width: 75px; border: solid cyan">a</div>
  <div class="stretch" style="width: 75px; border: solid magenta">b</div>
</div>
<div class="container">
  <div class="stretch" style="width: 75px; border: solid cyan">a</div>
  <div style="border: solid orange; height: 150px"></div>
  <div class="stretch" style="width: 75px; border: solid magenta">b</div>
</div>
<div class="container">
  <div style="width: 75px; border: solid cyan;">a</div>
  <div style="width: 75px; border: solid magenta">b</div>
</div>
<div class="container">
  <div style="width: 75px; border: solid cyan;">a</div>
  <div style="border: solid orange; height: 150px"></div>
  <div style="width: 75px; border: solid magenta">b</div>
</div>

Gecko, Blink and WebKit agree:

  • When computing the size of the flex lines: height: stretch stretches against the containing block, while align-self: stretch doesn't stretch.
  • Once we know the size of the flex lines: height: stretch keeps stretching against the containing block, while align-self: stretch stretches against the line.

If the flex container doesn't have a definite height (remove height: 100px):

  • Gecko and Blink don't stretch height: stretch, neither when computing line sizes nor once we know their size. align-self stretches in the latter one:

  • WebKit does that thing of stretching all ancestors with indefinite size, in this case filling the window:

Activity

  1. Loirooriol commented on Feb 26, 2025

    @Loirooriol
    ContributorAuthor

    BTW, same for a column flexbox:

    <!DOCTYPE html>
    <style>
    .container { display: flex; flex-direction: column; width: 100px; height: 100px; border: solid; flex-wrap: wrap; align-content: start }
    .stretch { width: -moz-available; width: -webkit-fill-available; width: stretch }
    </style>
    <div class="container">
      <div class="stretch" style="height: 75px; border: solid cyan">a</div>
      <div class="stretch" style="height: 75px; border: solid magenta">b</div>
    </div>
    <div class="container">
      <div class="stretch" style="height: 75px; border: solid cyan">a</div>
      <div style="border: solid orange; width: 150px"></div>
      <div class="stretch" style="height: 75px; border: solid magenta">b</div>
    </div>
    <div class="container">
      <div style="height: 75px; border: solid cyan;">a</div>
      <div style="height: 75px; border: solid magenta">b</div>
    </div>
    <div class="container">
      <div style="height: 75px; border: solid cyan;">a</div>
      <div style="border: solid orange; width: 150px"></div>
      <div style="height: 75px; border: solid magenta">b</div>
    </div>

    With width: min-content; max-width: 100px:

  2. changed the title [-][css-sizing] It's false that `height: stretch` behaves as `stretch` alignment[/-] [+][css-sizing][css-flexbox] It's false that `height: stretch` behaves as `stretch` alignment[/+] on Mar 10, 2025
  3. Loirooriol commented on Mar 10, 2025

    @Loirooriol
    ContributorAuthor

    Maybe I could have merged this into #11293. But this one is more about flexbox, that one about abspos.

  4. tabatkins commented on Mar 18, 2025

    @tabatkins
    Member

    I chatted with @bfgeek about this today.

    Currently our behavior for flex items is to treat width/height:stretch as equivalent to width/height:100%, rather than equivalent to stretch alignment. This equivalency is correct in most other places, but differs here, as you point out. However, the equivalency is also broken in abspos, where %s resolve against the original CB size but stretch alignment (and width:auto) resolves against the IMCB; Chrome implements width/height:stretch to fill the IMCB, matching stretch alignment.

    So, I'm inclined to say we should try and follow the spec's text. It's consistent with the behavior in all other locations, afaik, and presents a more useful behavior for this case. It does mean widening the delta between stretch and -webkit-fill-available, so there's potentially some compat risk, but I'd like to try for it if possible.

    However, if it turns out this is an issue, I'm okay with resolving to match the 100% behavior in this case instead.

  5. Loirooriol commented on Mar 18, 2025

    @Loirooriol
    ContributorAuthor

    Note that making a stretch size match stretch alignment presumably means that fit-content should also stretch to the line, but clamping between min-content and max-content. This might introduce some compat risk, but probably fine.

    Chrome implements width/height:stretch to fill the IMCB, matching stretch alignment.

    False, see #11293

  6. changed the title [-][css-sizing][css-flexbox] It's false that `height: stretch` behaves as `stretch` alignment[/-] [+][css-sizing][css-flexbox] It's false that `stretch` size behaves as `stretch` alignment[/+] on Mar 18, 2025
  7. davidsgrogan commented on Apr 23, 2025

    @davidsgrogan
    Member

    Heads up @dholbert , this issue is talking about changing stretch in the cross axis direction so that it resolves to the flex line's available space, not to the flex container's available space. If we do this, it would directly contradict your test assertion at https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/web_tests/external/wpt/css/css-sizing/stretch/stretch-block-size-001.html;l=189-191;drc=910dc12bbc0595ea727c435b8298998ccc53e965

    WDYT?

  8. dholbert commented on Apr 25, 2025

    @dholbert
    Member

    Thanks for the heads-up! No concerns from me on changing that testcase's expectations.

    When I wrote that test, I don't think I was aware of the linked spec text (the text that gives stretch sizes special meaning in contexts where *-self alignment applies), and I was just thinking of stretch as being a fancy 100% similar to how @tabatkins described it above.

  9. bfgeek commented on Apr 25, 2025

    @bfgeek

    So while experimenting with implementing this we've come to the conclusion that its not true that align-self: stretch and width: stretch are always equal.

    Specifically the difference comes from:

    Determine the hypothetical cross size of each item by performing layout as if it were an in-flow block-level box with the used main size and the given available space, treating auto as fit-content.

    https://drafts.csswg.org/css-flexbox-1/#algo-cross-item

    For example:

    <!DOCTYPE html>
    <div style="font: 16px monospace; width: 100px; height: 100px; border: solid 4px; display: flex; gap: 10px; flex-wrap: wrap; flex-direction: column;">
      <div style="background: lime;">a b c </div>
      <div style="background: lime;">a b c d e f g h i j k l m n o p q r s t u v w x y z</div>
    </div>
    

    https://www.software.hixie.ch/utilities/js/live-dom-viewer/?saved=13715

    When determining the line-cross-size of a wrapping flexbox (typically of a column flexbox) the given available-size is always the container available size.

    In this stage width: auto becomes width: fit-content. The line-cross-size is calculated based on this fact.

    Then when determining the final used cross size (the final layout of the flex-item) width: auto becomes width: stretch.
    This comes from:

    Determine the used cross size of each flex item. If a flex item has align-self: stretch, its computed cross size property is auto, and neither of its cross-axis margins are auto, the used outer cross size is the used cross size of its flex line, clamped according to the item’s used min and max cross sizes. Otherwise, the used cross size is the item’s hypothetical cross size.

    https://drafts.csswg.org/css-flexbox-1/#algo-stretch

    width: stretch doesn't have the ability to change, and hence can result in a different layout in these circumstances.

    IMO I think this behaviour difference is fine/expected. I don't think we want to magically coerce stretch to fit-content in the cross-size determination step.

    (Also note that passing an indefinite available size doesn't work as that'll make the width max-content instead of fit-content which is a sutble behaviour difference).

    I think that then the part of the sizing spec:

    Produces the same size that would be obtained by the self-alignment property in that axis being set to stretch and all sizing properties and aspect-ratio being set to their initial values.

    https://drafts.csswg.org/css-sizing-4/#stretch-fit-sizing

    ... is mostly true, but incorrect when it comes to flexbox for the above reason. (There are larger implications for any behaviour change away from this when we have things like min-width: stretch; width: fit-content for example).

  10. davidsgrogan commented on May 7, 2025

    @davidsgrogan
    Member

    In recent chrome canary we changed stretch to behave closer to align-self: stretch. Blink now mostly stretches cross-size:stretch to the line, not the container. But we arguably* don't completely follow the current spec, and think it's better behavior not to, as @bfgeek mentions above.

    Our deviation from the current spec (and, as a corollary, our spec-change proposal) can be summed up as

    • If flex-line cross size is known, cross-size:stretch stretches to it
    • Otherwise (e.g. when computing the flex-line cross-size), cross-size:stretch stretches to the container width

    Whereas the current spec arguably* requires

    • If flex-line cross size is known, cross-size:stretch stretches to it
    • Otherwise, cross-size:stretch behaves as fit-content with container cross size as available size

    Revisiting Oriol's example in #11784 (comment): (live):

    <!DOCTYPE html>
    <style>
    .container { display: flex; flex-direction: column; width: 100px; height: 100px; border: solid; flex-wrap: wrap; align-content: start }
    .stretch { width: -moz-available; width: -webkit-fill-available; width: stretch }
    </style>
    <div class="container">
      <div class="stretch" style="height: 75px; border: solid cyan">a</div>
      <div class="stretch" style="height: 75px; border: solid magenta">b</div>
    </div>
    <div class="container">
      <div class="stretch" style="height: 75px; border: solid cyan">a</div>
      <div style="border: solid orange; width: 150px"></div>
      <div class="stretch" style="height: 75px; border: solid magenta">b</div>
    </div>
    <div class="container">
      <div style="height: 75px; border: solid cyan;">a</div>
      <div style="height: 75px; border: solid magenta">b</div>
    </div>
    <div class="container">
      <div style="height: 75px; border: solid cyan;">a</div>
      <div style="border: solid orange; width: 150px"></div>
      <div style="height: 75px; border: solid magenta">b</div>
    </div>

    Blink's old behavior:
    Image
    Blink's new behavior:
    Image
    What the spec arguably* calls for:
    Image

    In some of these flex items, the author asked to stretch, and the current spec arguably* says to match align-self:stretch, but that results in no stretching at all. We argue that not stretching when the author requested it is lame; we should give them some version of what they want when we can.

    @dholbert @Loirooriol WDYT? Do you prefer Blink's "new" behavior, or the "spec" behavior?

    PS: On mentions of arguably*: I say arguably, because there's actually an interpretation of the spec that dictates our proposed behavior already. Namely, emphasis added,

    If used in an axis where the relevant self-alignment property applies to the element

    Produces the same size that would be obtained by the self-alignment property in that axis being set to stretch and all sizing properties and aspect-ratio being set to their initial values.

    Otherwise, if used in an axis where percentage sizes can resolve to a definite value

    Behaves as 100%, except it sizes the margin box regardless of the value of box-sizing.

    When we are calculating the the flex-line cross-size, align-self doesn't apply! So we follow the 'Otherwise' 100% behavior. When we do so, the line ends up being the same width as the container, which has the same effect of stretching to the container in the end.

  11. 18 remaining items

  12. Loirooriol commented on Aug 7, 2025

    @Loirooriol
    ContributorAuthor
  13. Loirooriol commented on Aug 9, 2025

    @Loirooriol
    ContributorAuthor

    This wasn't discussed in detail, but I guess the resolution implies that align-self: stretch makes the automatic size be fit-content (instead of stretch) while the cross line size isn't known. Otherwise it would stretch to the container, breaking various WPT and possibly websites.

    <!DOCTYPE html>
    <style>
    .container { display: flex; flex-flow: column wrap; width: 100px; height: 100px; border: solid; align-content: start }
    .container > div { align-self: stretch; background: cyan }
    .container > div > canvas { display: block; width: 100% }
    </style>
    <div class="container">
      <div style="width: auto">
        <canvas width="50" height="50"></canvas>
      </div>
    </div>
    <div class="container">
      <div style="width: stretch">
        <canvas width="50" height="50"></canvas>
      </div>
    </div>
    Blink, Servo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions