29

I think this is a tough one.

I use a grid system utilizing float:left. I could rewrite it with display:inline-block, but that would not change a thing.

Let's say we have two columns:

<div class="row"> 
    <div class="column">
        <!-- some content here -->
    </div>
    <div class="column">
        <!-- some content here -->
    </div>
</div>

If I put a block element with margin-top in it (like <h1>) I get non collapsing margins to the content before. This is normal as it is always as such with floated elements (or display: inline-block).

But I want to have collapsing margins. I tried a lot to make it work, but it seems that every CSS that will put two elements next to each other will destroy collapsing margins to the contents above.

I know, I could use CSS to get the first-child of an element to get rid of the margin-top. But in this case it won't apply, because the content is built with a CMS and there could be an arbitrary level of element depth till I get to the element.

Is there any way of doing this without JavaScript?

Fiddle: http://jsfiddle.net/HerrSerker/ZSV3D/

You can see, that the margin-top of h1 and margin-bottom of .header do not collapse. This is by means of float:left of .column.

.header {
  font-size: 24px;
  background: rgba(0,255,0,0.1);
}
h1 {
  background: silver;
  margin-bottom: 50px;
  font-size: 28px;
}
.column {
  float: left;
  width: 50%;
  background: rgba(255,0,0,0.1);
}
h2 {
  background: gold;
  margin-top: 50px;
  font-size: 24px;
}
<div class="header"><h1>&lt;h1>Headerh1&lt;/h1></h1></div>
<div class="row">
    <div class="column"><h2>&lt;h2>Col 1, float: left&lt;/h2></h2></div>
    <div class="column"><h2>&lt;h2>Col 2, float: left&lt;/h2></h2></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. If it would work, you wouldn't see the light red above col1 and col2</p>

Edit

I could of course use some successor operator in CSS like header + .row .column h1 { margin-top: 0;}. But that's not what I want. What I want is a way of settings element next to each other which work with margin-collapse of contained elements.


Edit2

So the situation and the question once again in short.
The problem is rather simple. I have some CSS code, which allows me to set two or more divs next to each other like float:left; width:50%. Inside of these divs are elements like h2 which have a top-margin. If inside a div before there is a h1 with bottom-margin. This situation does not allow the margins of h1 and h2 to collapse. Is there any chance of putting elements next to each other with margin-collapse and without setting the margin to zero manually?

Or otherwise. Is there any chance of settings elements next to each other without creating a new block formatting context?


Edit3:

-------------------------------------------------------------      
What it is:

 ┌─ .header ─────────────────┐
 │ ┌─ h1 ──────────────────┐ │
 │ │                       │ │
 │ └───────────────────────┘ │  ┄┄┄┬┄┄┄
 └───────────────────────────┘     ┆
                                   ┆ margin-bottom of h1
                                   ┆ 
 ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┼┄┄┄
                                   ┆
                                   ┆ margin-top  of h2
 ┌─ .row ────────────────────┐     ┆ not collapsing
 │ ┌─ .col ───┐ ┌─ .col ───┐ │  ┄┄┄┴┄┄┄
 │ │ ┌─ h2 ─┐ │ │ ┌─ h2 ─┐ │ │
 │ │ └──────┘ │ │ └──────┘ │ │
 │ └──────────┘ └──────────┘ │
 └───────────────────────────┘
      
-------------------------------------------------------------      
What I want:

 ┌─ .header ─────────────────┐
 │ ┌─ h1 ──────────────────┐ │
 │ │                       │ │
 │ └───────────────────────┘ │  ┄┄┄┬┄┄┄
 └───────────────────────────┘     ┆ margin-bottom of h1
                                   ┆ and margin-top  of h2
 ┌─ .row ────────────────────┐     ┆ collapsing
 │ ┌─ .col ───┐ ┌─ .col ───┐ │     ┆
 │ │ ┌─ h2 ─┐ │ │ ┌─ h2 ─┐ │ │  ┄┄┄┴┄┄┄
 │ │ └──────┘ │ │ └──────┘ │ │
 │ └──────────┘ └──────────┘ │
 └───────────────────────────┘
      
-------------------------------------------------------------      
Community
  • 1
  • 1
yunzen
  • 32,854
  • 11
  • 73
  • 106
  • 3
    No CSS and no fiddle ? :/ – Andrea Ligios Sep 23 '13 at 15:51
  • 1
    Uh, I see... I don't think so :| +1, this *is* a tough one – Andrea Ligios Sep 24 '13 at 08:31
  • 1
    If I understand what you want (I'm not sure I do), the container just needs to have a float too. – JackHasaKeyboard Jun 15 '16 at 08:58
  • 1
    Is this an issue only when you have a `.column` immediately following the `.header`? If so you could build a bit more of a specific css override for the `h1` elements inside it. Something like: `.header + .row h1` – Adam Hughes Jun 15 '16 at 09:23
  • 3
    Just realised this question is from 2013 - :( – Adam Hughes Jun 15 '16 at 09:25
  • 1
    @AdamHughes, but it edited and bounty added today. It still relevant, I think. – vp_arth Jun 15 '16 at 09:28
  • 3
    3 years solving this problem? :))) – Legionar Jun 15 '16 at 09:50
  • @JackHasaKeyboard Nope. Doesn't work: See http://jsfiddle.net/HerrSerker/ZSV3D/5/ – yunzen Jun 15 '16 at 10:45
  • As I understand its possible with negative margins: .row { margin-top: -50px;} – Javier Gonzalez Jun 15 '16 at 12:49
  • Still cannot understand the question :/ – Medet Tleukabiluly Jun 19 '16 at 12:31
  • @MedetTleukabiluly The problem is rather simple. I have some CSS code, which allows me to set two or more `div`s next to each other like `float:left; width:50%`. Inside of these `div`s are elements like `h2` which have a `top-margin`. If inside a div before there is a `h1` with `bottom-margin`. This situation does not allow the margins of `h1` and `h2` to collapse. Is there any chance of putting elements next to each other with margin-collapse and without setting the margin to zero manually? – yunzen Jun 20 '16 at 07:16
  • @HerrSerker Collapse like at certain viewport width .column of col2 should be placed correctly below col1. – frnt Jun 20 '16 at 07:28
  • @frnt Sorry, I read you comment several times and my question. I cannot understand what you mean. – yunzen Jun 20 '16 at 07:31
  • @HerrSerker I too actually didn't understand your question and even there is no h2 in your fiddle so I was bit confused. So what I was saying is that whether you want your col2 text to be placed below col1 when we reduce our browser width. – frnt Jun 20 '16 at 07:42
  • @frnt I changed my code to have h1 and h2 in it. And regarding you question. Yes the cols should be adaptive. In mobile view they should be full with. – yunzen Jun 20 '16 at 08:23
  • @HerrSerker okay thanks. – frnt Jun 20 '16 at 09:17
  • Maybe [this](http://alistapart.com/article/axiomatic-css-and-lobotomized-owls) helps – Caio Felipe Pereira Jun 21 '16 at 17:09

11 Answers11

17

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.

twxia
  • 1,813
  • 1
  • 15
  • 25
5

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;
}
Gust van de Wal
  • 5,211
  • 1
  • 24
  • 48
3

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.

Adam Hughes
  • 2,197
  • 20
  • 31
3

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>
SiNONiMiTY
  • 675
  • 3
  • 14
  • In this case I don't have a choice. If you use Bootstrap or Foundation you are bound to float – yunzen Jun 15 '16 at 11:16
  • 2
    You cheated. setting the `margin` of `h1` to zero is not the solution – yunzen Jun 15 '16 at 11:18
  • What a word you got there mate, I did not cheat. The code is up there, visible... Anyways, I edited the answer. Why don't you try the solution above. If you are still insistent on collapsing the margins with other possible methods and found an answer, kindly share it here. – SiNONiMiTY Jun 15 '16 at 11:44
  • 1
    Sorry for the strong word. But you did it again. Setting the margin of `firstcol` to zero is not collapsing the margins. – yunzen Jun 20 '16 at 07:35
2

According to Mozzilla Developer Network:

Margins of floating and absolutely positioned elements never collapse.

If the first element is guaranteed to be in need of a collapse inside an arbitrary number of elements, your best bet would be the following CSS:

.header + * :first-child {
  margin-top: 0;
}

If I understand the question correctly, this is as close as you will get with CSS alone.

Demo:

.header {
    background: silver;
    height: 50px;
    margin-bottom: 50px;
}
.column {
    float: left;
    width: 50%;
}
h1 {
    background: gold;
    margin-top: 50px;
}
.header + * :first-child {
  margin-top: 0;
}
<div class="header">Header</div>
<div class="row">
    <div class="column"><h1>Col 1</h1></div>
    <div class="column"><h1>Col 2</h1></div>
</div>
SeinopSys
  • 8,787
  • 10
  • 62
  • 110
2

Will display: table / table-row / table-cell be an option?

.header {
  background: silver;
  height: 50px;
  margin-bottom: 50px;
  font-size: 24px;
  display: table-row;
}
.row {
  display: table-row;
}
.column {
  display: table-cell;
  width: 50%;
}
h1 {
  background: gold;
  margin-top: 50px;
  font-size: 24px;
}
<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>
Asons
  • 84,923
  • 12
  • 110
  • 165
  • 1
    This looke d promissing, but 1. `table-row` cannot be 100% width. 2.This is not margin-collapse: `table-row`just disables the margin of header. – yunzen Jun 17 '16 at 15:12
2
.header + *:last-child {
      h1{
         margin-bottom:0px;
       }
}
 .row + * :first-child{
       h2{
         margin-top:0px;
      }
}
Narottam
  • 39
  • 4
1

Documentation about margin collapse is pretty clear...

Margins of floating and absolutely positioned elements never collapse.

https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Mastering_margin_collapsing

Even using flexbox will not help, the behavior remain the same...

If you know which elements have a margin-top, maybe you can get something out of first-of-type selector... See snippets :

.header {
  background: silver;
  height: 50px;
  margin-bottom: 50px;
  font-size: 24px;
}
.column {
  float: left;
  width: 50%;
}
h1 {
  background: gold;
  margin-top: 50px;
  font-size: 24px;
}
.column h1:first-of-type {
  margin-top: 0;
}
<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>
ben
  • 3,558
  • 1
  • 15
  • 28
1

Give *{margin:0;} style in your css. It will avoid all default margins in all browsers.

Mani
  • 2,675
  • 2
  • 20
  • 42
1

See Default display property of h1 is as below,

h1{
margin-top: 0.67em;/* which is around 11px*/
margin-bottom: 0.67em;/* which is around 11px*/
margin-left: 0;
margin-right: 0;
}

So instead of setting margin top or bottom for your h1 or h2, you can set margin-top for row, which get's you same result as h1 and h2 assigned margin value. Now when you resize your browser with this as mentioned in below codes.You could notice that while resizing it space get's decreased and increased. But at one point it will get stop as we have assigned margin top + we already have default h1 and h2 margin too.

.header {
font-size: 24px;
  background: rgba(0,255,0,0.1);
}
h1 {
  background: silver;
  margin-bottom: 50px;
  font-size: 28px;
}
.column {
  float: left;
  width: 50%;
  background: rgba(255,0,0,0.1);
}
h2 {
 background: gold;
  margin-top: 50px;
  font-size: 24px;
}
.row{
  margin-top:7.5%;
}

But if we remove h1 and h2 default value, then the space between .header and .column would be the space assigned to .row(in %) and that decreases as we resize our browser width. Now to assign width of 100% to .column can be done using media query by targeting particular mobile devices. So that col 2 get placed below col 1 with width of 100%

body{
margin:0;
}
.header {
  font-size: 24px;
  background: rgba(0,255,0,0.1);
}
h1 {
  background: silver;
  font-size: 28px;
  margin:0;
}
.column {
  float: left;
  width: 50%;
  background: rgba(255,0,0,0.1);
}
h2 {
  background: gold;
  font-size: 24px;
  margin:0;
  }
.row{
  margin-top:7.5%;
}
frnt
  • 8,455
  • 2
  • 22
  • 25
1

As you have heard several times already... This doesn't seem to be possible...

I suspect you don't want to use a selector so as to not affect all h1 and solve for other elements ( like maybe h2, or even a p ) that may not be h1 and may not be adjacent siblings of .header element ( maybe just h1 without .header wrapper, or a p with margin bottom 70px )...

Assuming I'm right... You can use this selector to solve for all such cases...

.row .column > *:first-child {
  margin-top: 0;
}

This will set top-margin of first element in each column of every row to 0.

Think this is closest you can get to the desired behaviour...

Lemme know if that helps.

shramee
  • 5,030
  • 1
  • 22
  • 46