Visitar URL original
[css-sizing] Intrinsic sizes for stretchable replaced elements · Issue #13149 · w3c/csswg-drafts · GitHub
Skip to content

[css-sizing] Intrinsic sizes for stretchable replaced elements #13149

Open

Description

@Loirooriol

https://drafts.csswg.org/css2/#inline-replaced-width

If height and width both have computed values of auto and the element has an intrinsic ratio but no intrinsic height or width, then the used value of width is undefined in CSS 2. However, it is suggested that, if the containing block’s width does not itself depend on the replaced element’s width, then the used value of width is calculated from the constraint equation used for block-level, non-replaced elements in normal flow.

This doesn't explain how min-content and friends are supposed to behave.

Testcase
<!DOCTYPE html>
<style>svg { display: block; outline: 3px solid; margin: 12px 0; }</style>
<div style="width: 100px">
  <svg viewbox="0 0 1 1" style="width: auto"></svg>
  <svg viewbox="0 0 1 1" style="width: min-content"></svg>
  <svg viewbox="0 0 1 1" style="width: fit-content"></svg>
  <svg viewbox="0 0 1 1" style="width: max-content"></svg>
  <svg viewbox="0 0 1 1" style="width: 0; min-width: min-content"></svg>
  <svg viewbox="0 0 1 1" style="width: 0; min-width: fit-content"></svg>
  <svg viewbox="0 0 1 1" style="width: 0; min-width: max-content"></svg>
  <svg viewbox="0 0 1 1" style="width: 200px; max-width: min-content"></svg>
  <svg viewbox="0 0 1 1" style="width: 200px; max-width: fit-content"></svg>
  <svg viewbox="0 0 1 1" style="width: 200px; max-width: max-content"></svg>
  <hr>
  <svg viewbox="0 0 1 1" style="height: 50px; width: auto"></svg>
  <svg viewbox="0 0 1 1" style="height: 50px; width: min-content"></svg>
  <svg viewbox="0 0 1 1" style="height: 50px; width: fit-content"></svg>
  <svg viewbox="0 0 1 1" style="height: 50px; width: max-content"></svg>
  <svg viewbox="0 0 1 1" style="height: 50px; width: 0; min-width: min-content"></svg>
  <svg viewbox="0 0 1 1" style="height: 50px; width: 0; min-width: fit-content"></svg>
  <svg viewbox="0 0 1 1" style="height: 50px; width: 0; min-width: max-content"></svg>
  <svg viewbox="0 0 1 1" style="height: 50px; width: 200px; max-width: min-content"></svg>
  <svg viewbox="0 0 1 1" style="height: 50px; width: 200px; max-width: fit-content"></svg>
  <svg viewbox="0 0 1 1" style="height: 50px; width: 200px; max-width: max-content"></svg>
  <hr>
  <svg viewbox="0 0 1 1" style="min-height: 150px; width: auto"></svg>
  <svg viewbox="0 0 1 1" style="min-height: 150px; width: min-content"></svg>
  <svg viewbox="0 0 1 1" style="min-height: 150px; width: fit-content"></svg>
  <svg viewbox="0 0 1 1" style="min-height: 150px; width: max-content"></svg>
  <svg viewbox="0 0 1 1" style="min-height: 150px; width: 0; min-width: min-content"></svg>
  <svg viewbox="0 0 1 1" style="min-height: 150px; width: 0; min-width: fit-content"></svg>
  <svg viewbox="0 0 1 1" style="min-height: 150px; width: 0; min-width: max-content"></svg>
  <svg viewbox="0 0 1 1" style="min-height: 150px; width: 200px; max-width: min-content"></svg>
  <svg viewbox="0 0 1 1" style="min-height: 150px; width: 200px; max-width: fit-content"></svg>
  <svg viewbox="0 0 1 1" style="min-height: 150px; width: 200px; max-width: max-content"></svg>
  <hr>
  <svg viewbox="0 0 1 1" style="max-height: 50px; width: auto"></svg>
  <svg viewbox="0 0 1 1" style="max-height: 50px; width: min-content"></svg>
  <svg viewbox="0 0 1 1" style="max-height: 50px; width: fit-content"></svg>
  <svg viewbox="0 0 1 1" style="max-height: 50px; width: max-content"></svg>
  <svg viewbox="0 0 1 1" style="max-height: 50px; width: 0; min-width: min-content"></svg>
  <svg viewbox="0 0 1 1" style="max-height: 50px; width: 0; min-width: fit-content"></svg>
  <svg viewbox="0 0 1 1" style="max-height: 50px; width: 0; min-width: max-content"></svg>
  <svg viewbox="0 0 1 1" style="max-height: 50px; width: 200px; max-width: min-content"></svg>
  <svg viewbox="0 0 1 1" style="max-height: 50px; width: 200px; max-width: fit-content"></svg>
  <svg viewbox="0 0 1 1" style="max-height: 50px; width: 200px; max-width: max-content"></svg>
  <hr>
  <svg viewbox="0 0 1 1" style="min-height: 50px; max-height: 150px; width: auto"></svg>
  <svg viewbox="0 0 1 1" style="min-height: 50px; max-height: 150px; width: min-content"></svg>
  <svg viewbox="0 0 1 1" style="min-height: 50px; max-height: 150px; width: fit-content"></svg>
  <svg viewbox="0 0 1 1" style="min-height: 50px; max-height: 150px; width: max-content"></svg>
  <svg viewbox="0 0 1 1" style="min-height: 50px; max-height: 150px; width: 0; min-width: min-content"></svg>
  <svg viewbox="0 0 1 1" style="min-height: 50px; max-height: 150px; width: 0; min-width: fit-content"></svg>
  <svg viewbox="0 0 1 1" style="min-height: 50px; max-height: 150px; width: 0; min-width: max-content"></svg>
  <svg viewbox="0 0 1 1" style="min-height: 50px; max-height: 150px; width: 200px; max-width: min-content"></svg>
  <svg viewbox="0 0 1 1" style="min-height: 50px; max-height: 150px; width: 200px; max-width: fit-content"></svg>
  <svg viewbox="0 0 1 1" style="min-height: 50px; max-height: 150px; width: 200px; max-width: max-content"></svg>
</div>
<script>
for (let el of document.querySelector("div").children) {
  if (el.tagName === "svg") {
    document.body.append(el.clientWidth + "x" + el.clientHeight, document.createElement("br"));
  } else {
    document.body.append(el.cloneNode());
  }
}
</script>
Results
Style (stretch size = 100px) Gecko WebKit Proposal 1 Blink Proposal 2
width: auto 100x100 100x100 100x100 100x100 100x100
width: min-content 0x0 300x300 300x300 100x100 100x100
width: fit-content 0x0 300x300 300x300 100x100 100x100
width: max-content 100x100 300x300 300x300 100x100 100x100
width: 0; min-width: min-content 0x0 300x300 300x300 0x0 100x100
width: 0; min-width: fit-content 0x0 300x300 300x300 0x0 100x100
width: 0; min-width: max-content 0x0 300x300 300x300 0x0 100x100
width: 200px; max-width: min-content 0x0 200x200 200x200 200x200 100x100
width: 200px; max-width: fit-content 0x0 200x200 200x200 200x200 100x100
width: 200px; max-width: max-content 0x0 200x200 200x200 200x200 100x100
height: 50px; width: auto 50x50 50x50 50x50 50x50 50x50
height: 50px; width: min-content 50x50 300x50 50x50 50x50 50x50
height: 50px; width: fit-content 50x50 300x50 50x50 50x50 50x50
height: 50px; width: max-content 50x50 300x50 50x50 50x50 50x50
height: 50px; width: 0; min-width: min-content 50x50 300x50 50x50 50x50 50x50
height: 50px; width: 0; min-width: fit-content 50x50 300x50 50x50 50x50 50x50
height: 50px; width: 0; min-width: max-content 50x50 300x50 50x50 50x50 50x50
height: 50px; width: 200px; max-width: min-content 50x50 200x50 50x50 50x50 50x50
height: 50px; width: 200px; max-width: fit-content 50x50 200x50 50x50 50x50 50x50
height: 50px; width: 200px; max-width: max-content 50x50 200x50 50x50 50x50 50x50
min-height: 150px; width: auto 150x150 150x150 150x150 150x150 150x150
min-height: 150px; width: min-content 0x150 300x300 300x300 150x150 150x150
min-height: 150px; width: fit-content 0x150 300x300 300x300 150x150 150x150
min-height: 150px; width: max-content 150x150 300x300 300x300 150x150 150x150
min-height: 150px; width: 0; min-width: min-content 0x150 300x300 300x300 150x150 150x150
min-height: 150px; width: 0; min-width: fit-content 0x150 300x300 300x300 150x150 150x150
min-height: 150px; width: 0; min-width: max-content 0x150 300x300 300x300 150x150 150x150
min-height: 150px; width: 200px; max-width: min-content 0x150 200x200 200x200 200x200 150x150
min-height: 150px; width: 200px; max-width: fit-content 0x150 200x200 200x200 200x200 150x150
min-height: 150px; width: 200px; max-width: max-content 0x150 200x200 200x200 200x200 150x150
max-height: 50px; width: auto 50x50 150x50 50x50 50x50 50x50
max-height: 50px; width: min-content 0x0 300x50 50x50 50x50 50x50
max-height: 50px; width: fit-content 0x0 300x50 50x50 50x50 50x50
max-height: 50px; width: max-content 50x50 300x50 50x50 50x50 50x50
max-height: 50px; width: 0; min-width: min-content 0x0 300x50 50x50 0x0 50x50
max-height: 50px; width: 0; min-width: fit-content 0x0 300x50 50x50 0x0 50x50
max-height: 50px; width: 0; min-width: max-content 0x0 300x50 50x50 0x0 50x50
max-height: 50px; width: 200px; max-width: min-content 0x0 200x50 50x50 50x50 50x50
max-height: 50px; width: 200px; max-width: fit-content 0x0 200x50 50x50 50x50 50x50
max-height: 50px; width: 200px; max-width: max-content 0x0 200x50 50x50 50x50 50x50
min-height: 50px; max-height: 150px; width: auto 100x100 50x50 100x100 100x100 100x100
min-height: 50px; max-height: 150px; width: min-content 0x50 300x150 150x150 100x100 100x100
min-height: 50px; max-height: 150px; width: fit-content 0x50 300x150 150x150 100x100 100x100
min-height: 50px; max-height: 150px; width: max-content 100x100 300x150 150x150 100x100 100x100
min-height: 50px; max-height: 150px; width: 0; min-width: min-content 0x50 300x150 150x150 50x50 100x100
min-height: 50px; max-height: 150px; width: 0; min-width: fit-content 0x50 300x150 150x150 50x50 100x100
min-height: 50px; max-height: 150px; width: 0; min-width: max-content 0x50 300x150 150x150 50x50 100x100
min-height: 50px; max-height: 150px; width: 200px; max-width: min-content 0x50 200x150 150x150 150x150 100x100
min-height: 50px; max-height: 150px; width: 200px; max-width: fit-content 0x50 200x150 150x150 150x150 100x100
min-height: 50px; max-height: 150px; width: 200px; max-width: max-content 0x50 200x150 150x150 150x150 100x100

Gecko doesn't seem to make much sense to me.

WebKit is buggy, but it seems to handle this behavior during the resolution of the automatic inline size. But it's not as simple as resolving to stretch (instead of fit-content as usual for replaced), because that would ignore size constraints in the other axis.

So my proposal 1 is that the automatic size is:

  • If height is definite, same as min/fit/max-content, i.e. the height (clamped between min/max-height) transferred thru the aspect ratio
  • Otherwise, similar to min/fit/max-content but using the stretch size instead of the 300px default object width. That is, stretch size clamped by the transferred min/max-height

Blink follows a different approach, and handles the stretching as part of the min/max-content sizes. Going in that direction, my first naive idea was:

  • The automatic size is fit-content (as usual for replaced elements)
  • The min-content size is 0px (*)
  • The max-content size is calc(1px / 0) (*)
  • Thus fit-content stretches to the container

(*) Clamped between the transferred min-height and max-height. Or if height is definite, just the transferred clamped height.

But letting max-content explode isn't great and probably not web compatible.

Blink seems to do:

  • If the height is definite, the min/max-content sizes are the height (clamped between min/max-height) transferred thru the aspect ratio (as usual).
  • Otherwise, the min/max-content sizes are indefinite. The keywords behave as such:
    • In min-width, they behave as the min-height transferred thru the aspect ratio
    • In max-width, they behave as the max-height (floored by the min-height) transferred thru the aspect ratio
    • In width, they behave as the stretch size (or zero when computing the intrinsic contributions), clamped by the transferred min/max-height

I'm not a fan of these keywords having different behaviors on different properties. I would rather use the width behavior in min-width and max-width too. That's my proposal 2.

Activity

  1. moved this to FTF agenda items in CSSWG January 2026 meetingon Jan 22, 2026
  2. tabatkins commented on Jan 23, 2026

    @tabatkins
    Member

    Expanding on the testcase a bit with some labels, and an aspect-ratio div with some text content for comparison:

    testcase source
    <!DOCTYPE html>
    <style>
    svg, svg+div { display: block; outline: 3px solid; margin: 12px 0; background: #8006; }
    svg+div { outline-color: orange; }
    </style>
    <div style="width: 100px; background: #0002">
      height: auto;
      <svg viewbox="0 0 1 1" style="width: auto"></svg>  <div style="width: auto; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="width: min-content"></svg>  <div style="width: min-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="width: fit-content"></svg>  <div style="width: fit-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="width: max-content"></svg>  <div style="width: max-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="width: 0; min-width: min-content"></svg>  <div style="width: 0; min-width: min-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="width: 0; min-width: fit-content"></svg>  <div style="width: 0; min-width: fit-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="width: 0; min-width: max-content"></svg>  <div style="width: 0; min-width: max-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="width: 200px; max-width: min-content"></svg>  <div style="width: 200px; max-width: min-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="width: 200px; max-width: fit-content"></svg>  <div style="width: 200px; max-width: fit-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="width: 200px; max-width: max-content"></svg>  <div style="width: 200px; max-width: max-content; aspect-ratio: 1;">xxx xxxx</div>
      <hr>
      height: 50px;
      <svg viewbox="0 0 1 1" style="height: 50px; width: auto"></svg>  <div style="height: 50px; width: auto; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="height: 50px; width: min-content"></svg>  <div style="height: 50px; width: min-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="height: 50px; width: fit-content"></svg>  <div style="height: 50px; width: fit-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="height: 50px; width: max-content"></svg>  <div style="height: 50px; width: max-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="height: 50px; width: 0; min-width: min-content"></svg>  <div style="height: 50px; width: 0; min-width: min-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="height: 50px; width: 0; min-width: fit-content"></svg>  <div style="height: 50px; width: 0; min-width: fit-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="height: 50px; width: 0; min-width: max-content"></svg>  <div style="height: 50px; width: 0; min-width: max-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="height: 50px; width: 200px; max-width: min-content"></svg>  <div style="height: 50px; width: 200px; max-width: min-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="height: 50px; width: 200px; max-width: fit-content"></svg>  <div style="height: 50px; width: 200px; max-width: fit-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="height: 50px; width: 200px; max-width: max-content"></svg>  <div style="height: 50px; width: 200px; max-width: max-content; aspect-ratio: 1;">xxx xxxx</div>
      <hr>
      min-height: 150px;
      <svg viewbox="0 0 1 1" style="min-height: 150px; width: auto"></svg>  <div style="min-height: 150px; width: auto; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="min-height: 150px; width: min-content"></svg>  <div style="min-height: 150px; width: min-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="min-height: 150px; width: fit-content"></svg>  <div style="min-height: 150px; width: fit-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="min-height: 150px; width: max-content"></svg>  <div style="min-height: 150px; width: max-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="min-height: 150px; width: 0; min-width: min-content"></svg>  <div style="min-height: 150px; width: 0; min-width: min-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="min-height: 150px; width: 0; min-width: fit-content"></svg>  <div style="min-height: 150px; width: 0; min-width: fit-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="min-height: 150px; width: 0; min-width: max-content"></svg>  <div style="min-height: 150px; width: 0; min-width: max-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="min-height: 150px; width: 200px; max-width: min-content"></svg>  <div style="min-height: 150px; width: 200px; max-width: min-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="min-height: 150px; width: 200px; max-width: fit-content"></svg>  <div style="min-height: 150px; width: 200px; max-width: fit-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="min-height: 150px; width: 200px; max-width: max-content"></svg>  <div style="min-height: 150px; width: 200px; max-width: max-content; aspect-ratio: 1;">xxx xxxx</div>
      <hr>
      max-height: 50px;
      <svg viewbox="0 0 1 1" style="max-height: 50px; width: auto"></svg>  <div style="max-height: 50px; width: auto; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="max-height: 50px; width: min-content"></svg>  <div style="max-height: 50px; width: min-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="max-height: 50px; width: fit-content"></svg>  <div style="max-height: 50px; width: fit-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="max-height: 50px; width: max-content"></svg>  <div style="max-height: 50px; width: max-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="max-height: 50px; width: 0; min-width: min-content"></svg>  <div style="max-height: 50px; width: 0; min-width: min-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="max-height: 50px; width: 0; min-width: fit-content"></svg>  <div style="max-height: 50px; width: 0; min-width: fit-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="max-height: 50px; width: 0; min-width: max-content"></svg>  <div style="max-height: 50px; width: 0; min-width: max-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="max-height: 50px; width: 200px; max-width: min-content"></svg>  <div style="max-height: 50px; width: 200px; max-width: min-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="max-height: 50px; width: 200px; max-width: fit-content"></svg>  <div style="max-height: 50px; width: 200px; max-width: fit-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="max-height: 50px; width: 200px; max-width: max-content"></svg>  <div style="max-height: 50px; width: 200px; max-width: max-content; aspect-ratio: 1;">xxx xxxx</div>
      <hr>
      min-height: 50px; max-height: 150px;
      <svg viewbox="0 0 1 1" style="min-height: 50px; max-height: 150px; width: auto"></svg>  <div style="min-height: 50px; max-height: 150px; width: auto; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="min-height: 50px; max-height: 150px; width: min-content"></svg>  <div style="min-height: 50px; max-height: 150px; width: min-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="min-height: 50px; max-height: 150px; width: fit-content"></svg>  <div style="min-height: 50px; max-height: 150px; width: fit-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="min-height: 50px; max-height: 150px; width: max-content"></svg>  <div style="min-height: 50px; max-height: 150px; width: max-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="min-height: 50px; max-height: 150px; width: 0; min-width: min-content"></svg>  <div style="min-height: 50px; max-height: 150px; width: 0; min-width: min-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="min-height: 50px; max-height: 150px; width: 0; min-width: fit-content"></svg>  <div style="min-height: 50px; max-height: 150px; width: 0; min-width: fit-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="min-height: 50px; max-height: 150px; width: 0; min-width: max-content"></svg>  <div style="min-height: 50px; max-height: 150px; width: 0; min-width: max-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="min-height: 50px; max-height: 150px; width: 200px; max-width: min-content"></svg>  <div style="min-height: 50px; max-height: 150px; width: 200px; max-width: min-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="min-height: 50px; max-height: 150px; width: 200px; max-width: fit-content"></svg>  <div style="min-height: 50px; max-height: 150px; width: 200px; max-width: fit-content; aspect-ratio: 1;">xxx xxxx</div>
      <svg viewbox="0 0 1 1" style="min-height: 50px; max-height: 150px; width: 200px; max-width: max-content"></svg>  <div style="min-height: 50px; max-height: 150px; width: 200px; max-width: max-content; aspect-ratio: 1;">xxx xxxx</div>
    </div>
    <script>
    for (let el of document.querySelector("div").children) {
      if (el.tagName === "svg") {
        document.body.append(el.clientWidth + "x" + el.clientHeight, document.createElement("br"));
      } else {
        document.body.append(el.cloneNode());
      }
    }
    </script>

    link to dom viewer

  3. moved this from FTF agenda items to Tuesday afternoon in CSSWG January 2026 meetingon Jan 26, 2026
  4. moved this from Tuesday afternoon to Skipped things in CSSWG January 2026 meetingon Jan 28, 2026
  5. moved this from Skipped things to Wednesday morning in CSSWG January 2026 meetingon Jan 28, 2026
  6. Loirooriol commented on Feb 16, 2026

    @Loirooriol
    ContributorAuthor

    Actually, this depends on #12333. Because if we accept that replaced elements resolve intrinsic keywords in the block axis in order to resolve intrinsic keywords in the inline axis, then the behavior that we get is basically the same as in my proposal 2 above. Basically, the process is:

    1. Find the tentative inline size that we would get if all sizing properties (in both axes) were set to their initial values. In this case, it's the stretch size
    2. Find the tentative block size by looking at the sizing properties in the block axis, resolving intrinsic keywords to the result of transferring (1) thru the aspect ratio
    3. Find the final inline size by looking at the sizing properties in the inline axis, resolving intrinsic keywords to the result of transferring (2) thru the aspect ratio
    4. Find the final block size by looking at the sizing properties in the block axis, resolving intrinsic keywords to the result of transferring (3) thru the aspect ratio

    So we don't really need to redefine min/max-content to get this behavior, we get it automatically. Since at (2) we can resolve everything, (3) gets an exact sizing constraint from the block axis, so that's the only choice for min/max-content. I'm quite puzzled that Blink does this weird thing of varying the behavior depending on the property, and WebKit has a completely different behavior.

    Stretching only for auto but not for min/max-content only makes sense if in #12333 we decide to make replaced elements consistent with non-replaced ones. Then at (2) we can't resolve intrinsic keywords, so (3) can indeed get just a min and a max constraints from the block axis, thus being able to choose which value to pick within that interval (e.g. stretch size or 300px).

  7. 16 remaining items

  8. fantasai commented on May 27, 2026

    @fantasai
    Collaborator

    ... Just realized I partway analyzed this issue, but didn't finish. :/ Here's my notes on what I thought would make the most sense after walking through the table.

    1. Calculate the intrinsic widths (each independently):
      • If there is a known height (specified or natural), pipe that through the aspect ratio as the intrinsic size and resolve.
      • Otherwise, min-content is zero and max-content is infinity capped by the container width (if definite), else by the container height (if definite) piped through the aspect ratio, otherwise 300px.
        • Floor/cap these values by min-height/max-height piped through aspect ratio, if known/definite.
    2. Calculate the intrinsic heights (each independently): Resolve the width, and pipe through the aspect ratio.

    I didn't get to the "compare notes to the state of the conversation and check that it makes sense before writing something up" stage though.

    Image
  9. css-meeting-bot commented on May 27, 2026

    @css-meeting-bot
    Member

    The CSS Working Group just discussed [css-sizing] Intrinsic sizes for stretchable replaced elements.

    The full IRC log of that discussion <kbabbitt> oriol: as we resolved some other issue, when you have a replaced element with preferred aspect ratio and no natural sizes it should stretch in inline axis by default
    <kbabbitt> ... however it's not clear how this should happen
    <kbabbitt> ... while CSS2 describes something like this, in CSS2 sizes could only be length percentage auto, now we have min-content etc.
    <kbabbitt> ... so the interaction of all this is unclear
    <kbabbitt> ... browsers also don't really agree
    <kbabbitt> ... have various test cases in the issue
    <kbabbitt> ... I couldn't figure out what Gecko is even trying to do, but webkit apparently tries to put stretching behavior into auto size
    <kbabbitt> ... if you use min-content etc, it uses the 300px default object width
    <kbabbitt> ... doesn't exactly match this but roughly seems to be the idea
    <kbabbitt> ... Blink seems to, if I understood from conversation with ian, there's no min content or max content size
    <kbabbitt> ... instead each property falls back to some behavior
    <kbabbitt> ... [gives examples]
    <kbabbitt> ... in all cases, constraints transfer from one axis to other
    <kbabbitt> ... what I did in Servo is something closer to Blink than others
    <kbabbitt> ... but like Blink we do resolve min-content and max-content sizes
    <kbabbitt> ... more concretely when resolving intrinsic keywords in inline size, we transfer a tentative block size
    <kbabbitt> ... if it has intrinsic keywords, takes them from intrinsic tentative inline size that we resolve as stretch sizer
    <kbabbitt> ... difference between Blink and Servo ends up being, sizing keywords same in all properties, while in Blink it depends on which property youre using the keyword on
    <kbabbitt> ... iank_ has some opinions
    <kbabbitt> ... mostly want to see what people think in general, what's the right approach here?
    <JohnJansen> can you share the test cases?
    <kbabbitt> iank_: I can try to frame it somewhat differently
    <kbabbitt> ... stretchable replaced elements, we never use the default object size
    <kbabbitt> ... that is compat constrained
    <kbabbitt> ... we only use it in 1 very weird case I can get into later
    <kbabbitt> ... broadly speaking we don't use the 300x150 default intrinsic size
    <kbabbitt> ... question is, how do these things stretch
    <kbabbitt> ... broadly 2 options
    <kbabbitt> ... when you are computing min-content or max-content on width
    <kbabbitt> ... this resolves to stretch size
    <kbabbitt> ... and this has other implications
    <kbabbitt> ... other way to thinkm about it, what Blink does
    <kbabbitt> ... because there's no intrinsic size, min/max-content resolves to 0
    <kbabbitt> ... and at the very end of the replaced sizing steps we go, this didn't produce a size, we'll now stretch it and reflect stretched size to aspect ratio
    <kbabbitt> ... final fixup step we do
    <kbabbitt> ... that's how I think about it
    <kbabbitt> ... behavior also worth mention is, where we treat min-content as indefinite, we already have 4 things ... min-content in block axis for example
    <kbabbitt> ... we already have this concept of min-content is indefinite at various stages of length sizing algorithm
    <kbabbitt> ... I wrote in a comment a more elaborate description of Blink's replaced sizing algorithm
    <kbabbitt> astearns: fantasai you just added a table of comparative results?
    <kbabbitt> fantasai: yes, I went through table and asked, what do I most expect to happen
    <kbabbitt> ... then, what would be underlying principle that would get me there
    <kbabbitt> ... tried to analyze this issue and took some notes but didn't finish by going back to analyze state of discussion and what iank_ had written
    <kbabbitt> ... copied out my notes for discussion but haven't cross-checked it with everything else that's been discussed yet to see what makes sense
    <sgill> q+
    <kbabbitt> ... I think iank_'s algorithm makes sense, some of the results seem a bit odd
    <kbabbitt> ... for exmample the fact that width: min-content gets you 100x100
    <kbabbitt> ... but width: 0 min-width: min-content gets you 0x0 is a bit weird
    <kbabbitt> iank_: I can explain why that is
    <kbabbitt> ... min-content isn't resolving
    <kbabbitt> ... there's no height constraints to speak of
    <kbabbitt> ... so as a result, min-content is indefinite
    <kbabbitt> ... and then at the very end of the algorithm we need to assign some width to it
    <kbabbitt> ... and so we stretch
    <kbabbitt> ... that's what's happening in both cases
    <kbabbitt> ... width: 0px or 10px, we have a size, we've resolved it
    <kbabbitt> ... min-width hasn't resolved, no reasonable intrinsic size we can use
    <kbabbitt> ... so it falls back to initial 0px
    <kbabbitt> ... for width case, we weren't able to resolve it, so it falls back to stretching
    <kbabbitt> ... same as auto
    <kbabbitt> fantasai: I think your algo makes sense, not convinced result entirely makes sense
    <kbabbitt> ... I think what might make sense would be for you and me and TabAtkins to go over this with a whiteboard
    <kbabbitt> iank_: ok I'm fine with that
    <kbabbitt> fantasai: if oriol wants to dial in too
    <kbabbitt> iank_: I'd be hesitent to make min-content and max-content resolve to differen things
    <kbabbitt> ... conceptually for replaced elements they're the same
    <sgill> q-
    <kbabbitt> ... but we can go through this on a whiteboard
    <kbabbitt> ... stretchable replaced elements are weird because they don't fall back to replaced object size except for one weird compat constrained case
    <kbabbitt> astearns: other comments or questions before we move on?
    <kbabbitt> sgill: initially had something but as I thought through it I lost it, will wait for results of fantasai and co to get together
    <kbabbitt> astearns: we'll let the subgroup go through the variations in the table and come back to the group with a concrete proposal
  10. tabatkins commented on May 28, 2026

    @tabatkins
    Member

    Okay, @Loirooriol @bfgeek @fantasai and I chatted about this. Here's the proposal:

    In the situation described here (replaced element with only a natural aspect-ratio, no other natural sizes):

    1. For widths:
      • min-content keyword resolves to 0
      • max-content keyword resolves to the stretch-fit size; if the stretch-fit size cannot resolve, then it resolves to either zero or 300px depending on whether the element is a compressible replaced element.
    2. For heights: transfer the width through the aspect ratio.
    3. If within calc-size(), resolve that here.
    4. After resolving the min/max/preferred sizes as defined above, transfer min/max-size constraints from the other axis per Sizing 4, section 4.4. (In other words, these values are squishy, like auto, not hard, like an explicit stretch value.)
    5. Finally clamp the used preferred width by the same-axis used min and max sizes to get the used sizes.

    (This is skipping a little bit; it's meant to just be a slight modification to what Ian posted in #13149 (comment))

    Update: Proposed to swap steps 3 & 4.

  11. fantasai commented on Jun 10, 2026

    @fantasai
    Collaborator

    On further reflection, I think transferred size clamping should go before calc-size() resolution. Otherwise we're not able to effectively transition between 0 and auto.

  12. moved this to TPAC/FTF agenda items in CSSWG agenda TPAC 2026on Oct 12, 2026
  13. moved this from TPAC/FTF agenda items to Friday Afternoon in CSSWG agenda TPAC 2026on Oct 21, 2026
  14. css-meeting-bot commented on Oct 30, 2026

    @css-meeting-bot
    Member

    The CSS Working Group just discussed [css-sizing] Intrinsic sizes for stretchable replaced elements, and agreed to the following:

    • RESOLVED: Adopt proposal in https://github.com/w3c/csswg-drafts/issues/13149#issuecomment-4569048300 including the swap of 3 & 4
    The full IRC log of that discussion <TabAtkins> oriol: I think last status was that we'd come up with a proposal
    <TabAtkins> oriol: then we had a proposal but fantasai said it should be changed to make calc-size() correctly interpolate between 0 and auto, which I kinda agree
    <TabAtkins> fantasai: we hadn't put it in front of the wg
    <TabAtkins> fantasai: but if you think kit's right, we're probably fine
    <TabAtkins> fantasai: I suggest we talk the group through it
    <astearns> summary comment: https://github.com//issues/13149#issuecomment-4569048300
    <TabAtkins> oriol: to summarize a bit, when you have a replaced element with an aspect-ratio and no width/height, it stretches by default
    <TabAtkins> oriol: then need to define how this translates into min-content/max-content/etc
    <TabAtkins> oriol: so idea was, min-content is zero, max-content is stretch-fit size, but if we can't get a stretch size, we do 0 or 300px depending on whether it's compressible
    <TabAtkins> oriol: for heights, we transfer inline sizes thru the aspect ratio
    <TabAtkins> oriol: for calc-size()...
    <TabAtkins> fantasai: we swap 3 and 4
    <TabAtkins> oriol: typically we only resolve the keywords on [...]
    <TabAtkins> oriol: but now in addition we transfer the sizing constraints from the other axis
    <TabAtkins> fantasai: then that gives us a size - what it would be with that keyword - then we apply calc-size() to modify it
    <TabAtkins> fantasai: and finally at the end we clamp by the same-axis min and max-size
    <TabAtkins> fantasai: so the four of us think these steps are the right way to handle this case
    <TabAtkins> fantasai: so we propose to adopt these steps. we'll figure out spec text in a bit
    <TabAtkins> astearns: so y'all are still in agreement?
    <TabAtkins> iank_: I think so....
    <TabAtkins> iank_: the min-content does change once you resolve the block size
    <TabAtkins> dbaron: what is the stretch-fit size?
    <TabAtkins> iank_: if you've got a definite available, it stretches
    <TabAtkins> fantasai: the margin box fit in to the available space
    <TabAtkins> dbaron: so max-content depends on container here?
    <TabAtkins> iank_: if it's definite
    <TabAtkins> dbaron: [skeptical face]
    <TabAtkins> dbaron: the original intent of min/max content is they're completely intrinsic, but we probably already broke that
    <TabAtkins> TabAtkins: I think for wrapping column flexboxes yeah
    <TabAtkins> iank_: this is the only case where we're using an *inline* size
    <TabAtkins> iank_: so fi you have fit-content on an a-r SVG, it'll stretch to fill a definite container, and this is answering how that works
    <TabAtkins> iank_: right now blink runs thru the replaced steps and at the end fails and just makes it stretch
    <TabAtkins> iank_: this moves it earlier in the algorithm
    <TabAtkins> dbaron: if the various implementors think it's ok I won't object. it's not my favorite thing to do, but it may well solve the problem we have
    <TabAtkins> iank_: the only other way to fix this is do unexplainable fixup at the end when we can't figure eout min/max size
    <fantasai> TabAtkins: The big deal is that we wanted calc-size() to work on the actual result had you given the basis
    <fantasai> ... that requires us to resolve the basis during layout, not at the end
    <TabAtkins> iank_: so if you can work out the replaced element's height, that's still the min/max content size
    <TabAtkins> emilio: I was looking at some of the non-calc-size() cases like fit-content
    <TabAtkins> emilio: are those also changing here?
    <TabAtkins> emilio: they seem all over the place
    <TabAtkins> emilio: gecko has changed in some spots
    <TabAtkins> astearns: okay resolving on this?
    <TabAtkins> emilio: I trust oriol and ian
    <TabAtkins> iank_: until we write test cases there will be slightly different interpretations of what we agreed on, anyway
    <fantasai> PROPOSED: Adopt proposal in https://github.com//issues/13149#issuecomment-4569048300 including the swap of 3 & 4
    <fantasai> RESOLVED: Adopt proposal in https://github.com//issues/13149#issuecomment-4569048300 including the swap of 3 & 4
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