Wanted: CSS grid system AND collapsing margins

This is impossible :O

According the spec (https://www.w3.org/TR/CSS2/box.html#collapsing-margins)

Collapsing Condition:

  • both belong to in-flow block-level boxes that participate in the same block formatting context
  • no line boxes, no clearance, no padding and no border separate them (Note that certain zero-height line boxes (see 9.4.2) are ignored for this purpose.)
  • both belong to vertically-adjacent box edges, i.e. form one of the following pairs:
    • top margin of a box and top margin of its first in-flow child
    • bottom margin of box and top margin of its next in-flow following sibling
    • bottom margin of a last in-flow child and bottom margin of its parent if the parent has 'auto' computed height
    • top and bottom margins of a box that does not establish a new block formatting context and that has zero computed 'min-height', zero or 'auto' computed 'height', and no in-flow children

block containers (such as inline-blocks, table-cells, and table-captions) that are not block boxes

Collapsing margins in these situation will break the rule:

  • Margins between a floated box and any other box do not collapse (not even between a float and its in-flow children).
  • 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 boxes 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 box with a 'height' of 'auto' and a 'min-height' of zero collapses with its last in-flow block-level child's bottom margin if the box has no bottom padding and no bottom border and the child's bottom margin does not collapse with a top margin that has clearance.
  • A box'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) collapse.

flex box also...(https://www.w3.org/TR/css-flexbox-1/#item-margins)

The margins of adjacent flex items do not collapse.

grid also...(https://www.w3.org/TR/css-grid-1/#item-margins)

the margins of adjacent grid items do not collapse.

It is impossible to collapse margin-top: 50px; by using one of these solution: inline-block, position: absolute, float, flex, grid.


If you can't set the margin to zero, maybe you can use many other method to break the margin function

for example: let h1 be inline and let div have gold background to break the margin-top: 50px;:

According the margin spec (https://www.w3.org/TR/CSS2/box.html#margin-properties):

Margin properties specify the width of the margin area of a box. The 'margin' shorthand property sets the margin for all four sides while the other margin properties only set their respective side. These properties apply to all elements, but vertical margins will not have any effect on non-replaced inline elements.


I don't think you can do that. Probably the best method would be to target the margin's based on a few conditions and remove it.

If this problem is only happening when the .column with h1 elements is immediately following .header, you could build a bit more of a specific CSS rule. This would override the margin and negate it.

.header + .row h1 {
    margin-top: 0;
}

There's quite a lot of if's in this answer though and without a bit more info it's quite difficult to solve.


The only option with which you actually collapse margins, without some selector trickery or JavaScript, is the following:

Don't set margin-top: 50px on your h1, but on .row

See this Fiddle

HTML

<div class="header">Header is normal div</div>
<div class="row">
    <div class="column"><h1>Col 1 is float: left</h1></div>
    <div class="column"><h1>Col 2 is float: left</h1></div>
</div>
<p>I want a 50 pixel margin between Header and the Cols, but the two margins don't collapse and I end up with 50 + = 100 pixel gap.</p>

CSS

.header {
  background: silver;
  height: 50px;
  margin-bottom: 50px;
  font-size: 24px;
}
.row {
  margin-top: 50px;
}
.column {
  float: left;
  width: 50%;
}
h1 {
  background: gold;
  font-size: 24px;
}

Make the .row element a flex, Problem solved...

Why do you want to float it?

.header {
  background: silver;
  height: 50px;
  margin-bottom: 50px;
  font-size: 24px;
}

.row {
	display: flex;
}

.column {
  width: 50%;
}
h1 {
  background: gold;
  font-size: 24px;
}

.firstcol {
   margin-top: 0;
}
<div class="header">Header is normal div</div>
<div class="row">
    <div class="column"><h1 class="firstcol">Col 1 is float: left</h1></div>
    <div class="column"><h1 class="firstcol">Col 2 is float: left</h1></div>
</div>
<p>I want a 50 pixel margin between Header and the Cols, but the two margins don't collapse and I end up with 50 + = 100 pixel gap.</p>