Skip to content

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

@Loirooriol

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 Gecko Blink WebKit
width: auto 100x100 100x100 100x100
width: min-content 0x0 100x100 300x300
width: fit-content 0x0 100x100 300x300
width: max-content 100x100 100x100 300x300
width: 0; min-width: min-content 0x0 0x0 300x300
width: 0; min-width: fit-content 0x0 0x0 300x300
width: 0; min-width: max-content 0x0 0x0 300x300
width: 200px; max-width: min-content 0x0 200x200 200x200
width: 200px; max-width: fit-content 0x0 200x200 200x200
width: 200px; max-width: max-content 0x0 200x200 200x200
height: 50px; width: auto 50x50 50x50 50x50
height: 50px; width: min-content 50x50 50x50 300x50
height: 50px; width: fit-content 50x50 50x50 300x50
height: 50px; width: max-content 50x50 50x50 300x50
height: 50px; width: 0; min-width: min-content 50x50 50x50 300x50
height: 50px; width: 0; min-width: fit-content 50x50 50x50 300x50
height: 50px; width: 0; min-width: max-content 50x50 50x50 300x50
height: 50px; width: 200px; max-width: min-content 50x50 50x50 200x50
height: 50px; width: 200px; max-width: fit-content 50x50 50x50 200x50
height: 50px; width: 200px; max-width: max-content 50x50 50x50 200x50
min-height: 150px; width: auto 150x150 150x150 150x150
min-height: 150px; width: min-content 0x150 150x150 300x300
min-height: 150px; width: fit-content 0x150 150x150 300x300
min-height: 150px; width: max-content 150x150 150x150 300x300
min-height: 150px; width: 0; min-width: min-content 0x150 150x150 300x300
min-height: 150px; width: 0; min-width: fit-content 0x150 150x150 300x300
min-height: 150px; width: 0; min-width: max-content 0x150 150x150 300x300
min-height: 150px; width: 200px; max-width: min-content 0x150 200x200 200x200
min-height: 150px; width: 200px; max-width: fit-content 0x150 200x200 200x200
min-height: 150px; width: 200px; max-width: max-content 0x150 200x200 200x200
max-height: 50px; width: auto 50x50 50x50 150x50
max-height: 50px; width: min-content 0x0 50x50 300x50
max-height: 50px; width: fit-content 0x0 50x50 300x50
max-height: 50px; width: max-content 50x50 50x50 300x50
max-height: 50px; width: 0; min-width: min-content 0x0 0x0 300x50
max-height: 50px; width: 0; min-width: fit-content 0x0 0x0 300x50
max-height: 50px; width: 0; min-width: max-content 0x0 0x0 300x50
max-height: 50px; width: 200px; max-width: min-content 0x0 50x50 200x50
max-height: 50px; width: 200px; max-width: fit-content 0x0 50x50 200x50
max-height: 50px; width: 200px; max-width: max-content 0x0 50x50 200x50
min-height: 50px; max-height: 150px; width: auto 100x100 100x100 50x50
min-height: 50px; max-height: 150px; width: min-content 0x50 100x100 300x150
min-height: 50px; max-height: 150px; width: fit-content 0x50 100x100 300x150
min-height: 50px; max-height: 150px; width: max-content 100x100 100x100 300x150
min-height: 50px; max-height: 150px; width: 0; min-width: min-content 0x50 50x50 300x150
min-height: 50px; max-height: 150px; width: 0; min-width: fit-content 0x50 50x50 300x150
min-height: 50px; max-height: 150px; width: 0; min-width: max-content 0x50 50x50 300x150
min-height: 50px; max-height: 150px; width: 200px; max-width: min-content 0x50 150x150 200x150
min-height: 50px; max-height: 150px; width: 200px; max-width: fit-content 0x50 150x150 200x150
min-height: 50px; max-height: 150px; width: 200px; max-width: max-content 0x50 150x150 200x150

Gecko and WebKit don't seem to make much sense to me.

I think what I would expect is:

  • 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

But letting max-content explode isn't great and maybe 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 is min-width and max-width too.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions