w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.51k stars 669 forks source link

[css2][css-flow][cssom] How does avoiding floats affect margins? #9174

Open Loirooriol opened 1 year ago

Loirooriol commented 1 year ago

https://drafts.csswg.org/css2/#floats

The border box of a table, a block-level replaced element, or an element in the normal flow that establishes a new block formatting context (such as an element with overflow other than visible) must not overlap the margin box of any floats in the same block formatting context as the element itself. If necessary, implementations should clear the said element by placing it below any preceding floats, but may place it adjacent to such floats if there is sufficient space.

So the vertical adjustment to avoid floats is done with clearance, but what mechanism is used for the horizontal adjustment?

I see 2 reasonable options:

  1. The float is avoided by changing the used value of margins
  2. The float is avoided by adding some extra space additional to the margins. These space should then be taken into account in the equation in https://drafts.csswg.org/css2/#blockwidth

The thing is that getComputedStyle exposes the used margins: https://drafts.csswg.org/cssom/#resolved-value-special-case-property-like-height

If the property applies to the element or pseudo-element and the resolved value of the display property is not none or contents, then the resolved value is the used value. Otherwise the resolved value is the computed value.

However, note that for Gecko, Blink and WebKit, the used value is only for a computed auto. For a computed <length-percentage>, they just absolutize it into a length, even if there is over-constrainment and the used value is a different amount. Servo still provides the used value even for a computed <length-percentage>.

Then, consider

<!DOCTYPE html>
<div style="border: solid; display: flow-root; width: 200px">
  <div style="float: left; width: 50px; height: 50px; background: cyan"></div>
  <div id="target" style="display: flow-root; height: 100px; background: magenta; margin-left: auto; margin-right: auto; width: 100px"></div>
</div>
<script>
document.body.append(getComputedStyle(target).marginLeft + ", " + getComputedStyle(target).marginRight);
</script>

Note that this is not clear in CSS2, but on all browsers the auto margins center the element within the space next to the float, not within the containing block:

This is what browsers say:

The behaviors of Gecko, Blink and WebKit seem nonsensical to me.

Also, if we want round-tripping, we must go with option 1 (or change how auto behaves in the presence of floats, but that seems more risky for compat).

bfgeek commented 1 year ago

Blink recently changed this to fix a bug - and now behave like gecko (partially due to the round-tripping issue you describe).

Ian

Loirooriol commented 1 year ago

So checking latest Blink I see that it always resolves auto to 0px (unlike Gecko, even if there are no floats).

This still doesn't round-trip: setting both margins to 0px in the example would place the element adjacent to the float instead of being separated by 25px.

Loirooriol commented 10 months ago

Blink changed again in https://chromium-review.googlesource.com/c/chromium/src/+/4930440 and aligned with Servo (option 1 above), which I think is the most reasonable behavior.

So Agenda+ to resolve on it. In particular:

css-meeting-bot commented 9 months ago

The CSS Working Group just discussed [css2][css-flow][cssom] How does avoiding floats affect margins?, and agreed to the following:

The full IRC log of that discussion <emilio> oriol: so CSS2 when translated in modern terminology, it says that a block level element which establishes an independent formatting context, shouldn't collapse its margins
<dbaron> s/collapse its margins/overlap the margin box of floats/
<emilio> ... it can do so by being moved next to the floats or below the floats
<emilio> ... if it's moved below then this is done by inserting clearance
<emilio> ... which is a well-defined concepts
<emilio> [room chuckles]
<emilio> dbaron: it's a defined concept
<emilio> oriol: problem is what to do in the inline axis
<emilio> ... by what mechanism are we placing this element next to the floats
<emilio> ... the proposal is to modify the used margin value to not overlap the floats
<emilio> ... we could introduce another concept instead
<emilio> ... I'd go with using margins
<emilio> ... this has one potential problem for the auto value
<emilio> ... because gCS exposes the used value of margins for auto
<emilio> ... (spec says always but they only do for auto).
<emilio> iank_: floats and margins don't work like you'd expect to
<emilio> ... margins start at the containing block edge
<emilio> not at the float edge
<emilio> ... so margins always are referring to the CB edge
<emilio> ... auto margins are different
<emilio> ... auto margins center in the area between the float and the edge of the CB
<emilio> oriol: right so question is what should getComputedStyle return
<emilio> ... gecko returns 0 and zero
<fantasai> [oriol reports the results of such testcases]
<fantasai> [they are various levels of broken]
<emilio> oriol: blink aligned with servo
<emilio> ... would like to resolve on them
<emilio> ... first proposed resolution would be that we avoid overlapping floats by changing the used margin
<emilio> iank_: don't wanna go down that road
<emilio> ... there are other mechanism that affect float positioning
<emilio> ... there's a lot of weird stuff in there, I think the current box is positioned
<emilio> oriol: but margin + border-box should be the width of the containing block
<emilio> ... should we drop that constraint?
<emilio> iank_: yes
<emilio> fantasai: that seems fine to drop in presence of floats
<emilio> q+
<emilio> astearns: this specific issue is about what value you report for gCS
<emilio> ... there are other things that affect placement of formatting contexts that affect that equation
<emilio> nicole: Can we get more consistent behavior across browsers without that constraint?
<emilio> iank_: yeah the biggest inconsistency is what to report for auto margins in gCS
<fantasai> emilio: is there inconsistency only in the presence of floats? e.g. if there's no floats, do we get reasonable results?
<emilio> iank_: in absence of floats you report the right values
<emilio> oriol: not sure if you clear what the reported behavior is
<fantasai> emilio: was curious about what the mechanisms are
<emilio> iank_: alignment of table cells
<emilio> ... might be a webkit-ism
<emilio> ... let me find the other ones
<astearns> ack fantasai
<Zakim> fantasai, you wanted to comment on alignment
<astearns> ack emilio
<emilio> fantasai: the border-box + margin-box filling the containing block we give up in the alignment spec via e.g. justify-self
<emilio> ... I think round-tripping via gCS would be great
<emilio> ... but I don't need we need to make the equation add up
<emilio> emilio: for round-tripping in this case you do need that equation to add up
<emilio> ... because the margin would start from the CB edge
<emilio> iank_: the other things that affect this is the webkit values of text-align
<emilio> ... which is equivalent to justify-self center
<emilio> dbaron: for the html alignment attrs?
<emilio> iank_: yes
<emilio> dbaron: gecko has similar ones
<emilio> oriol: I think we could resolve on auto margins centering in the remaining space which I don't think it's specified
<emilio> ... also that gCS should return a value that round trips
<emilio> ... so, align with blink and servo
<emilio> RESOLVED: Auto margins align between the remaining space after accounting for floats (existing interoperable behavior)
<dbaron> Unrelated side note: at least I still remember CSS2's float rules well enough to be able to write a testcase where browsers are not interoperable and get it to show non-interop on the first try:
<dbaron> https://software.hixie.ch/utilities/js/live-dom-viewer/?%3C!DOCTYPE%20html%3E%0A%3Cdiv%20style%3D%22width%3A%20200px%22%3E%0A%20%20%3Cdiv%20style%3D%22float%3A%20left%3B%20height%3A%20100px%3B%20width%3A%2010px%3B%20background%3A%20blue%22%3E%3C%2Fdiv%3E%0A%20%20%3Cdiv%20style%3D%22margin-left%3A%2020px%3B%20width%3A%2050px%3B%22%3E%0A%20%20%20%20%3Cdiv%20style%3D%22float%3A%20left%3B%20width%3A%2060px%3B%20height%3A%2020px%3B%20background%3A%2
<dbaron> 0fuchsia%22%3E%3C%2Fdiv%3E%0A%20%20%3C%2Fdiv%3E%0A%3C%2Fdiv%3E (WebKit follows the letter of the spec; Gecko and Chromium follow the spec's resummarization that follows "Loosely: ".)
<emilio> astearns: resolving on round-tripping doesn't seem super-useful if you're an implementor reading the spec
<emilio> astearns: proposal is to spec that auto-margin values round trip in getComputedStyle()
<emilio> fantasai: we don't know how, one is what oriol said, the other is returning "auto"
<emilio> astearns: might not be web compatible
<emilio> fantasai: specifically for the float case you could round-trip to auto
<emilio> iank_: no no no no
<emilio> ... the reason why we aligned to Servo's behavior
<emilio> ... we moved towards FF but too aggressively (return 0 for auto margins)
<emilio> ... and got bug reports
<emilio> ... so we need to return a decent used value
<emilio> fantasai: your proposed resolution has 2 interpretation, if only one is workable then we should write that down
<emilio> dbaron: are there any cases where there's no non-auto-value that round-trips?
<emilio> dbaron: can't think of one off the top of my head
<emilio> dbaron: part of the lesson is that we shouldn't write specs in terms of constraints because they have solutions that don't add up to what you thought
<emilio> dbaron: can we resolve on a goal rather than an exact spec?
<emilio> dbaron: if we resolve that we want ^
<emilio> fantasai: there shouldn't be two different ways of fixing that
<florian> q+
<fantasai> emilio: Proposed resolution 1: return used values, proposed resolution 2: they must round-trip
<astearns> ack florian
<emilio> florian: I think we shouldn't edit in css2
<emilio> ... wherever it goes, not css2
<florian> s/ not css2/ not css2: it is not in a publishable state, and after a few years, we evidently do not know how to get it to a publishable state
<emilio> RESOLVED: For elements with a principal box, getComputedStyle always returns used values
<emilio> s/used values/used values for margins
<emilio> RESOLVED: We'd like resolved margins to round-trip
<emilio> [rant about css2]
<bramus> ScribeNick: bramus