[CSS2.1] Clarifying 8.3.1 Collapsing Margins

This is for CSS2.1 Issue 159
   http://wiki.csswg.org/spec/css2.1#issue-159
triggered by this email
   http://lists.w3.org/Archives/Public/www-style/2010Feb/0015.html

It turns out, margin collapsing is very straightforward to define.
Most of the section is just giving examples of applying a small
handful of rules.

First, the complete reorganized text, which covers all but the last
two paragraphs of 8.3.1. This should give an overview of where we're
going. (Aside from a few minor editorial tweaks, all of the changes
after "Note the above rules imply that" are merely shifting things
around.)

  | In CSS, the adjoining margins of two or more boxes (which could be
  | next to one another or nested) can combine to form a single margin.
  | Margins that behave this way are called <dfn>collapsing margins</dfn>.
  |
  | Two margins are adjoining if and only if:
  |   * no non-empty content, padding or border areas; line boxes;
  |     or clearance separate them
  |   * both belong to normal-flow block-level boxes in the same block
  |     formatting context
  |
  | Note. Adjoining boxes may be generated by elements that are not
  | related as siblings or ancestors.
  |
  | Vertical margins collapse if they are adjoining. (Horizontal margins
  | never collapse.) However:
  |   * Margins of the root element's box do not collapse.
  |   * If the top and bottom margins of an element with clearance are
  |     adjoining, its margins collapse with the adjoining margins of
  |     subsequent siblings but that resulting margin does not collapse
  |     with the bottom margin of the parent block.
  |
  | Note the above rules imply that:
  |   * Vertical margins between a floated box and any other box do not
  |     collapse (not even between a float and its in-flow children).
  |   * Vertical margins of elements that establish new block formatting
  |     contexts (such as floats and elements with 'overflow' other than
  |     'visible') do not collapse with their in-flow children.
  |   * Margins of absolutely positioned boxes do not collapse (not even
  |     with their in-flow children).
  |   * Margins of inline-block elements do not collapse (not even with
  |     their in-flow children).
  |   * The bottom margin of an in-flow block-level element always
  |     collapses with the top margin of its next in-flow block-level
  |     sibling, unless that sibling has clearance.
  |   * The top margin of an in-flow block element collapses with
  |     its first in-flow block-level child's top margin if the
  |     element has no top border, no top padding, and the child has
  |     no clearance.
  |   * The bottom margin of an in-flow block element with a
  |     'height' of 'auto' collapses with its last in-flow block-level
  |     child's bottom margin if the element has no bottom padding or
  |     border.
  |   * An element's own margins collapse if the 'min-height' property
  |     is zero, and it has neither top or bottom borders nor top or
  |     bottom padding, and it has a 'height' of either 0 or 'auto', and
  |     it does not contain a line box, and all of its in-flow children's
  |     margins (if any) are adjoining.
  |
  | When two or more margins collapse, the resulting margin width is the
  | maximum of the adjoining margin widths. In the case of negative
  | margins, the maximum of the absolute values of the negative adjoining
  | margins is deducted from the maximum of the positive adjoining margins.
  | If there are no positive margins, the absolute maximum of the negative
  | adjoining margins is deducted from zero.
  |
  | If the top and bottom margins of a box are adjoining, then it is
  | possible for margins to collapse through it. In this case, the
  | position of the element depends on its relationship with the other
  | elements whose margins are being collapsed.
  |   * If the element's margins are collapsed with its parent's top
  |     margin, the top border edge of the box is defined to be the
  |     same as the parent's.
  |   * Otherwise, either the element's parent is not taking part in
  |     the margin collapsing, or only the parent's bottom margin is
  |     involved. The position of the element's top border edge is
  |     the same as it would have been if the element had a non-zero
  |     bottom border.
  | Note that the positions of elements that have been collapsed through
  | have no effect on the positions of the other elements with whose
  | margins they are being collapsed; the top border edge position is
  | only required for laying out descendants of these elements.

Now for the breakdown:

Replaced

  # In this specification, the expression collapsing margins means
  # that adjoining margins (no non-empty content, padding or border
  # areas or clearance separate them) of two or more boxes (which
  # may be next to one another or nested) combine to form a single
  # margin.

with

  | In CSS, the adjoining margins of two or more boxes (which could be
  | next to one another or nested) can combine to form a single margin.
  | Margins that behave this way are called <dfn>collapsing margins</dfn>.
  |
  | Two margins are adjoining if and only if:
  |   * no non-empty content, padding or border areas; line boxes;
  |     or clearance separate them
  |   * both belong to normal-flow block-level boxes in the same block
  |     formatting context

This has several effects:
   - Gets rid of awkwardness in definition of "collapsing margins"
   - Shifts out definition of "adjoining" into a list that we can
     add to, as dbaron suggested at the end of
       http://lists.w3.org/Archives/Public/www-style/2010Feb/0015.html
   - Adds (2nd item) a simple rule that generalizes almost all of
     of the subsequent rules in the section.

Replaced

  # In CSS 2.1, horizontal margins never collapse.
  #
  # Vertical margins may collapse between certain boxes:
  #   * Two or more adjoining vertical margins of block boxes in
  #     the normal flow collapse.

with

  | Vertical margins collapse if they are adjoining. (Horizontal margins
  | never collapse.)

in conjunction with the above definition of 'adjoining'.

Pulled

  #                               The resulting margin width is
  #     the maximum of the adjoining margin widths. In the case
  #     of negative margins, the maximum of the absolute values
  #     of the negative adjoining margins is deducted from the
  #     maximum of the positive adjoining margins. If there are
  #     no positive margins, the absolute maximum of the negative
  #     adjoining margins is deducted from zero.

out into its own paragraph about margin-collapsing math:

  | When two or more margins collapse, the resulting margin width is the
  | maximum of the adjoining margin widths. In the case of negative
  | margins, the maximum of the absolute values of the negative adjoining
  | margins is deducted from the maximum of the positive adjoining margins.
  | If there are no positive margins, the absolute maximum of the negative
  | adjoining margins is deducted from zero.

which is placed after all the conditions for when margins collapse.

Took

  #                                              Note. Adjoining
  #     boxes may be generated by elements that are not related
  #     as siblings or ancestors.

And tossed it higher, immediately after the definition of adjoining

  | Two margins are adjoining if and only if:
  |   ... [3-item list here] ...
  | Note. Adjoining boxes may be generated by elements that are not
  | related as siblings or ancestors.

The next four rules:

  #   * Vertical margins between a floated box and any other box
  #     do not collapse (not even between a float and its in-flow
  #     children).
  #   * Vertical margins of elements that establish new block
  #     formatting contexts (such as floats and elements with
  #     'overflow' other than 'visible') do not collapse with
  #     their in-flow children.
  #   * Margins of absolutely positioned boxes do not collapse
  #     (not even with their in-flow children).
  #   * Margins of inline-block elements do not collapse (not
  #     even with their in-flow children).

are special cases of

  |   * both belong to normal-flow block-level boxes in the same block
  |     formatting context

so I shoved them into a new list:

  | Note the above rules imply that:
  |   * Vertical margins between a floated box and any other box do not
  |     collapse (not even between a float and its in-flow children).
  |   * Vertical margins of elements that establish new block formatting
  |     contexts (such as floats and elements with 'overflow' other than
  |     'visible') do not collapse with their in-flow children.
  |   * Margins of absolutely positioned boxes do not collapse (not even
  |     with their in-flow children).
  |   * Margins of inline-block elements do not collapse (not even with
  |     their in-flow children).

The next rule is all about calculating the element's position:

  #   * If the top and bottom margins of a box are adjoining,
  #     then it is possible for margins to collapse through it.
  #     In this case, the position of the element depends on its
  #     relationship with the other elements whose margins are
  #     being collapsed.
  #       o If the element's margins are collapsed with its
  #         parent's top margin, the top border edge of the
  #         box is defined to be the same as the parent's.
  #       o Otherwise, either the element's parent is not
  #         taking part in the margin collapsing, or only the
  #         parent's bottom margin is involved. The position
  #         of the element's top border edge is the same as it
  #         would have been if the element had a non-zero bottom
  #         border.
  #     ... [[ see below for this paragraph ]] ...
  #     Note that the positions of elements that have been
  #     collapsed through have no effect on the positions of the
  #     other elements with whose margins they are being collapsed;
  #     the top border edge position is only required for laying
  #     out descendants of these elements.

I pulled it out into its own set of paragraphs at the end of all
the explanations of what margins collapse and how:

  | If the top and bottom margins of a box are adjoining, then it is
  | possible for margins to collapse through it. In this case, the
  | position of the element depends on its relationship with the other
  | elements whose margins are being collapsed.
  |   * If the element's margins are collapsed with its parent's top
  |     margin, the top border edge of the box is defined to be the
  |     same as the parent's.
  |   * Otherwise, either the element's parent is not taking part in
  |     the margin collapsing, or only the parent's bottom margin is
  |     involved. The position of the element's top border edge is
  |     the same as it would have been if the element had a non-zero
  |     bottom border.
  | Note that the positions of elements that have been collapsed through
  | have no effect on the positions of the other elements with whose
  | margins they are being collapsed; the top border edge position is
  | only required for laying out descendants of these elements.

The paragraph I removed from the middle of that section

  #     An element that has clearance never collapses its top
  #     margin with its parent block's bottom margin.

has very little to do with the rest of the section. I combined it with
the other statement on the same topic that's further down

  # When an element's own margins collapse, and that element has
  # clearance, its top margin collapses with the adjoining margins
  # of subsequent siblings but that resulting margin does not
  # collapse with the bottom margin of the parent block.

and took the combination, along with the next rule

  #   * Margins of the root element's box do not collapse.

and put them back up next to the statement of what margins collapse:

  | Vertical margins collapse if they are adjoining. (Horizontal margins
  | never collapse.) >>> However:
  |   * Margins of the root element's box do not collapse.
  |   * If the top and bottom margins of an element with clearance are
  |     adjoining, its margins collapse with the adjoining margins of
  |     subsequent siblings but that resulting margin does not collapse
  |     with the bottom margin of the parent block. <<<

The next three paragraphs after the list are merely illustrating
the rules that define "adjoining":

  # The bottom margin of an in-flow block-level element is always
  # adjoining to the top margin of its next in-flow block-level
  # sibling, unless that sibling has clearance.
  #
  # The top margin of an in-flow block-level element is adjoining
  # to its first in-flow block-level child's top margin if the
  # element has no top border, no top padding, and the child has
  # no clearance.
  #
  # The bottom margin of an in-flow block-level element with a
  # 'height' of 'auto' is adjoining to its last in-flow block-level
  # child's bottom margin if the element has no bottom padding or
  # border.
  #
  # An element's own margins are adjoining if the 'min-height'
  # property is zero, and it has neither top or bottom borders nor
  # top or bottom padding, and it has a 'height' of either 0 or
  # 'auto', and it does not contain a line box, and all of its
  # in-flow children's margins (if any) are adjoining.

So I replaced "is adjoining to" with "collapses with" for consistency
and stuffed them into the "implications" list:

  | Note the above rules imply that:
  |   ...
  |   * The bottom margin of an in-flow block-level element always
  |     collapses with the top margin of its next in-flow block-level
  |     sibling, unless that sibling has clearance.
  |   * The top margin of an in-flow block-level element collapses with
  |     its first in-flow block-level child's top margin if the
  |     element has no top border, no top padding, and the child has
  |     no clearance.
  |   * The bottom margin of an in-flow block-level element with a
  |     'height' of 'auto' collapses with its last in-flow block-level
  |     child's bottom margin if the element has no bottom padding or
  |     border.
  |   * An element's own margins collapse if the 'min-height' property
  |     is zero, and it has neither top or bottom borders nor top or
  |     bottom padding, and it has a 'height' of either 0 or 'auto', and
  |     it does not contain a line box, and all of its in-flow children's
  |     margins (if any) are adjoining.

(Note the conditions on the last two rules are implied particularly by
the "no non-empty content area" and "no line boxes" requirements, if
you're having trouble figuring out why it's "implied".)

~fantasai

Received on Thursday, 29 July 2010 03:01:48 UTC