Re: Issue 158 proposed text

This post is mainly just a pigeon-hole for my analysis of one of Bruno's
test cases.  But it does lead me to the following conclusions, of wider
interest:

(i) The change in 2007 from using top border to bottom border in
8.3.1-6.2 has indeed had subtle effects that were not immediately
obvious, just as Hixie feared.[1]  (However, the above change also
brought benefits, and it's not desirable or at all realistic to roll it
back.)

(ii) It's possible to mitigate the problem demonstrated in the test case
by changing how the hypothetical top border position of a self-adjoining
clearing element C is calculated when determining if clearance is
needed, just as Tab and Bruno have been discussing in this thread.

(iii) I buy Bruno's arguments in [3] and [4] (later in the thread than
this post that I'm replying to) that it's reasonable to calculate the
hypothetical position under the condition that C is prevented from
collapsing its bottom margin with its top margin via the temporary
assumption of a non-zero bottom border.

(iv) I also buy Tab's argument in [5] that such a change might be nice
to have, but another, simpler solution is to just ignore the problems
that self-adjoining clearing elements cause, by way of calculating the
hypothetical top border position under the sole assumption that C has
clear:none (so that all margins collapse normally during this calculation).


This is a very thorny topic, and its possibly reassuring that several of
us are iterating to the same conclusions -- although we are doing so on
the basis of the same test cases.  Now, any change to the current spec
is bound to create the occasional strange special cases, but given that
no-one seems too clear what the current spec is trying to say anyway
with its "preceding margins", proposing a change doesn't seem unreasonable.

By adopting Bruno's proposal, it appears that we're pushing the strange
cases largely out of sight.  It goes without saying, however, that
existing known corner-cases such as Hixie's and David Baron's test cases
should be carefully checked to ensure that neither of the proposed
changes alters the expected behaviour.

=====


Bruno Fassino wrote:
> On Mon, Jun 28, 2010 at 8:43 PM, Tab Atkins Jr. <jackalmage@gmail.com> wrote:
>> On Mon, Jun 28, 2010 at 11:01 AM, Bruno Fassino <fassino@gmail.com> wrote:
>>> On Mon, Jun 28, 2010 at 6:50 PM, Tab Atkins Jr. <jackalmage@gmail.com> wrote:
>>>> I added language to make it clear I'm explicitly defering to the
>>>> normal margin-collapse rules.  This isn't meant to be a special case;
>>>> it should lay out the element *exactly* like it wasn't clearing at
>>>> all.
>>>>
>>>> Is that sufficient?
>>>
>>> It is not explicitly stated what margins are considered during this
>>> collapsing to determine the hypothetical position.
>>> It is said "preceding margins", but I don't think this is precise enough.
>>>
>>> Consider something like this:
>>>
>>> <div style="background: lime; border-top: 1px solid">
>>>        <div style="float: left; background: blue; height: 50px; width: 100px"></div>
>>>        <div style="clear: left">
>>>                <div style="margin-top: 60px"></div>
>>>        </div>
>>> </div>
>>> <div style="background: yellow">next</div>
>>>
>>> (you can see it at http://brunildo.org/test/margin-collapse-clear-child-7.html)
>>>
>>> Should the top margin of the child of the div with clear, after the
>>> float, be considered in the computation of the hypothetical position?
>>> It collapses with the (null) top margin of the clear, but does not
>>> seem to be a "preceding" margin.
>>>
>>> However I doubt the intention is to really exclude it. At least
>>> Firefox and Safari seem to behave as if it was considered in the
>>> hypothetical position, and so as if no clearance was necessary here.
>> Again, the intention is to collapse all margins exactly as they would
>> normally be collapsed, as if clearance wasn't present.  Should I
>> remove the word "preceding" or something, if that's causing the
>> confusion?  Perhaps this would work better:
>>
>> """
>> Computing the clearance of an element on which 'clear' is set is done
>> by first determining the hypothetical position of the element's top
>> border edge within its parent block.  This position is determined
>> after the top margin of the element has been collapsed with all
>> appropriate adjoining margins, per normal margin-collapsing rules.
>> """
>>
>> ~TJ
>>
> 
> 
> Yes, without the word "preceding" there is no more confusion about its meaning.
> 
> The only problem is that I'm not sure that the intention is really to
> have the hypothetical position the same as the one with _all_ margins
> normally collapsed (i.e. the same as with clear: none).
> In other words, I'm not sure if the word "preceding" was initially
> there for any specific reason.
> 
> Consider this:
> 
> <div style="background: yellow">before</div>
> <div style="background: lime">
> 	<div style="float: left; height: 50px; width: 100px; background:blue"></div>
> 	<div style="clear: left; margin-bottom: 100px"></div>
> </div>
> <div style="background: yellow">next</div>
> 
> (you can see it at http://brunildo.org/test/margin-collapse-clear-child-9.html)
> What behavior do we want for this test case?

My back-of-a-large-envelope calculations are as follows.

First assume that the clearing element C was in fact clear:none. The
final collapsed margin in which C's margin participates would be 100px.
C's parent (the lime div) would have zero height.  We'd see a yellow
"before" followed by 100px of white space followed by a yellow "next".
In order to know where to position the float, we need to calculate the
top border position of the lime div.  8.3.1-6.2 applies, and the
position of its top border edge is the same as it would have been if it
had a non-zero bottom border.  This bottom border would be 100px below
"before", and hence so would the top border position due to the div's
zero height.  The top border position of the anonymous box around the
float, by the rules of 8.3.1-6.1, is the same as the top border position
  of the lime div; thus the top of the float would also be 100px below
"before", flush with the top of "next".

Tab's original proposal was to calculate the hypothetical position of
the top border of C under the conditions above.  8.3.1-6.1 applies, and
the top border position of C would be the same as that of its parent
lime div, 100px below "before" and flush with the top of the float and
the top of "next".  This is *bizarre*, and we shall come back to this
point later.  Since this hypothetical top border position is not past
the float, we introduce clearance, and should calculate its size.  We're
trying to "shift C up or down" as necessary so that its top border
position is the lower of "flush with the bottom of the float" and "flush
with its previously-determined hypothetical position"; the latter is
lower.  To know how much to shift, we need to know where the top border
position of C currently is, taking into account that we've now
introduced clearance and so C's top margin is prevented from collapsing
with the top margin of its parent lime div.  C's bottom margin doesn't
collapse with its parent's bottom margin either, due to the rule near
the bottom of 8.3.1.  Hence the parent lime div is precisely 100px high,
containing the uncollapsed 100px bottom margin of C, and is immediately
preceded by "before" and immediately followed by "next".  The position
of C's top border is currently the same as it would have been if C had a
non-zero bottom border (8.3.1-6.2), which is to say, at the top of the
lime div, flush with the bottom of "before".  So we finally know that we
need "move C downwards" by 100px (ie, the clearance is 100px) -- or more
accurately, put a 100px uncollapsable extra space above the combined
100px margin caused by C -- causing the parent lime div to be 200px high.

> With the new formulation the top border edge of the clear should be
> 100px lower than the bottom of "before" (hypothetical position), but
> then (since with clear there is no more collapsing with the parent
> top) we should have other 100px below this position. Or something like
> that... 

Not quite, but the right kind of idea, as we've just seen!  We've got a
200px-high lime div, which doesn't seem ideal.  (No browser gets this
right, as I'm sure you've already observed: Opera 10.53, Safari 4 and
and IE8 think the div should be 50px high [and goodness knows where
they're putting the 100px margin]; IE6 thinks the div should be 100px
high, whilst IE7 thinks it should be zero height but uses a 100px space
instead; Firefox 3.6 thinks the div should be 150px high.)

So what's actually going on in this test case?  The hypothetical
position of C's top border turned out to be quite low down.  This is
because its parent's temporary bottom border is used to determine its
top border position which in turn is used to determine C's hypothetical
top border position -- at the /bottom/ of the 100px combined margin
generated by C's bottom margin!  Once clearance was introduced, the
float moved up, as did C's top border position -- this time to the /top/
of the 100px combined margin.  But C was still required to move down by
100px :-/.  Surely we'd prefer it to only move down by 50px -- the
height of the float.

Who's to blame?  Well, the use of bottom border to determine the top
border of the lime div is the primary culprit.  Had top border been used
instead, as the spec used to require, then the top border position of
the lime div would have been directly below "before", as would the top
of the float and the hypothetical position of C's top border.  Clearance
would be introduced, which wouldn't affect the top border position of
the lime div, the float would stay where it was, the clearance would be
50px and C would "move down" to just below the float, as we might have
liked.

If nothing else, the latter result vindicates Hixie who expressed strong
reservations about changing from top border to bottom border in
8.3.1-6.2, stating that:[1]

"If we change these rules it could have very subtle effects that might
not be understood for years, at which point we'd be back to the same
position as we are in now: thinking the rules are unintuitive and
wanting to change them in another subtle way."

Now, that change meant that other test cases became more intuitive,
which was Alex Mogilevsky's motivation for starting that thread back in
2007 (and David Baron also produced the strong argument in [2] that it
gets along better with incremental rendering -- although I'm still in
the process of trying to understand the reasoning for that, since my
end-of-day brain seems to think exactly the opposite should be true).
So there's no reason to suggest that the change be rolled back, and
there's no way anyone would accept that anyway.

So what to do?  Probably, mitigate the problem by changing how the
hypothetical top border position of C is calculated when determining if
clearance is needed.  We should "push it downwards" somehow when C's
bottom margin is involved. This is exactly what Tab and Bruno were
discussing in the quoted section above.  What's more, I buy Bruno's
arguments in [3] and [4] (later in the thread than this post that I'm
replying to) that the hypothetical position should be calculated under
the condition that C is prevented from collapsing its bottom margin with
its top margin via the temporary assumption of a non-zero bottom border.


[1] http://lists.w3.org/Archives/Public/www-style/2007Sep/0046.html
[2] http://lists.w3.org/Archives/Public/www-style/2007Sep/0100.html
[3] http://lists.w3.org/Archives/Public/www-style/2010Jun/0643.html
[4] http://lists.w3.org/Archives/Public/www-style/2010Jun/0664.html
[5] http://lists.w3.org/Archives/Public/www-style/2010Jun/0651.html

Cheers,
Anton Prowse
http://dev.moonhenge.net

Received on Sunday, 4 July 2010 22:20:20 UTC