How do negative margins in CSS work and why is (margin-top:-5 != margin-bottom:5)?

CssMargin

Css Problem Overview


A common trick for vertical positioning elements is to use the following CSS:

.item {
    position:absolute;
    top:50%;
    margin-top:-8px; /* half of height */
    height: 16px;
}

When seen in the metric view as in Chrome this is what you see:

enter image description here

However, there is no visual margin depicted when you hover over the element i.e. the margin is 'outside' the border and can be visualized. But negative margins don't show up. How do they look and what is it that makes it different?

Why is margin-top:-8px not the same as margin-bottom:8px?

So just how do negative margins work and what's the intuition behind them. How do they 'bump up' (in case of margin-top < 0) an item?

Css Solutions


Solution 1 - Css

Negative margins are valid in css and understanding their (compliant) behaviour is mainly based on the box model and margin collapsing. While certain scenarios are more complex, a lot of common mistakes can be avoided after studying the spec.

For instance, rendering of your sample code is guided by the css spec as described in calculating heights and margins for absolutely positioned non-replaced elements.

If I were to make a graphical representation, I'd probably go with something like this (not to scale):

negative top margin

The margin box lost 8px on the top, however this does not affect the content & padding boxes. Because your element is absolutely positioned, moving the element 8px up does not cause any further disturbance to the layout; with static in-flow content that's not always the case.

Bonus:

Still need convincing that reading specs is the way to go (as opposed to articles like this)? I see you're trying to vertically center the element, so why do you have to set margin-top:-8px; and not margin-top:-50%;?

Well, vertical centering in CSS is harder than it should be. When setting even top or bottom margins in %, the value is calculated as a percentage always relative to the width of the containing block. This is rather a common pitfall and the quirk is rarely described outside of w3 docos

Solution 2 - Css

I'll try to explain it visually:

/**
 * explaining margins
 */

body {
  padding: 3em 15%
}

.parent {
  width: 50%;
  width: 400px;
  height: 400px;
  position: relative;
  background: lemonchiffon;
}

.parent:before,
.parent:after {
  position: absolute;
  content: "";
}

.parent:before {
  top: 0;
  bottom: 0;
  left: 50%;
  border-left: dashed 1px #ccc;
}

.parent:after {
  left: 0;
  right: 0;
  top: 50%;
  border-top: dashed 1px #ccc;
}

.child {
  width: 200px;
  height: 200px;
  background: rgba(200, 198, 133, .5);
}

ul {
  padding: 5% 20px;
}

.set1 .child {
  margin: 0;
  position: relative;
}

.set2 .child {
  margin-left: 75px;
  position: relative;
}

.set3 .child {
  margin-left: -75px;
  position: relative;
}


/* position absolute */

.set4 .child {
  top: 50%;
  left: 50%;
  margin: 0;
  position: absolute;
}

.set5 .child {
  top: 50%;
  left: 50%;
  margin-left: 75px;
  position: absolute;
}

.set6 .child {
  top: 50%; /* level from which margin-top starts 
	- downwards, in the case of a positive margin
	- upwards, in the case of a negative margin	
	*/
  left: 50%; /* level from which margin-left starts 
	- towards right, in the case of a positive margin
	- towards left, in the case of a negative margin	
	*/
  margin: -75px;
  position: absolute;
}

<!-- content to be placed inside <body>…</body> -->
<h2><code>position: relative;</code></h2>
<h3>Set 1</h3>
<div class="parent set 1">
  <div class="child">
    <pre>
.set1 .child {
  margin: 0;
  position: relative;
}
		</pre>
  </div>
</div>

<h3>Set 2</h3>
<div class="parent set2">
  <div class="child">
    <pre>
.set2 .child {
  margin-left: 75px;
  position: relative;
}
		</pre>
  </div>
</div>

<h3>Set 3</h3>
<div class="parent set3">
  <div class="child">
    <pre>
.set3 .child {
  margin-left: -75px;
  position: relative;
}
		</pre>
  </div>
</div>

<h2><code>position: absolute;</code></h2>

<h3>Set 4</h3>
<div class="parent set4">
  <div class="child">
    <pre>
.set4 .child {
  top: 50%;
  left: 50%;
  margin: 0;
  position: absolute;
}
		</pre>
  </div>
</div>

<h3>Set 5</h3>
<div class="parent set5">
  <div class="child">
    <pre>
.set5 .child {
  top: 50%;
  left: 50%;
  margin-left: 75px;
  position: absolute;
}
		</pre>
  </div>
</div>

<h3>Set 6</h3>
<div class="parent set6">
  <div class="child">
    <pre>
.set6 .child {
  top: 50%;
  left: 50%;
  margin: -75px;
  position: absolute;
}
		</pre>
  </div>
</div>

Solution 3 - Css

Margin is the spacing outside your element, just as padding is the spacing inside your element.

Setting the bottom margin indicates what distance you want below the current block. Setting a negative top margin indicates that you want negative spacing above your block. Negative spacing may in itself be a confusing concept, but just the way positive top margin pushes content down, a negative top margin pulls content up.

Solution 4 - Css

A margin-top of -8px means it will be 8px higher than if it had 0 margin.

A margin-bottom of 8px means that the thing below it will be 8px further down that if it had 0 margin.

Solution 5 - Css

good points already made here, but while there is lots of information about how rendering of margins is accomplished by the browser, the why isn't quite answered yet:

> "Why is margin-top:-8px not the same as margin-bottom:8px?"

what we also could ask is:

> Why doesn't a positive bottom margin 'bump up' preceding elements, whereas a positive top-margin 'bumps down' following elements?

so what we see is that there is a difference in the rendering of margins depending on the side they are applied to - top (and left) margins are different from bottom (and right) ones.

things are becoming clearer when having a (simplified) look at how styles are applied by the browser: elements are rendered top-down in the viewport, starting in the top left corner (let's stick with the vertical rendering for now, keeping in mind that the horizontal one is treated the same).

consider the following html:

<div class="box1"></div>
<div class="box2"></div>
<div class="box3"></div>

analogous to their position in code, these three boxes appear stacked 'top-down' in the browser (keeping things simple, we won't consider here the order property of the css3 'flex-box' module). so, whenever styles are applied to box 3, preceding element's positions (for box 1 and 2) have already been determined, and shouldn't be altered any more for the sake of rendering speed.

now, imagine a top margin of -10px for box 3. instead of shifting up all preceding elements to gather some space, the browser will just push box 3 up, so it's rendered on top of (or underneath, depending on the z-index) any preceding elements. even if performance wasn't an issue, moving all elements up could mean shifting them out of the viewport, thus the current scrolling position would have to be altered to have everything visible again.

same applies to a bottom margin for box 3, both negative and positive: instead of influencing already evaluated elements, only a new 'starting point' for upcoming elements is determined. thus setting a positive bottom margin will push the following elements down; a negative one will push them up.

Solution 6 - Css

Because you have used absolute positioning, and specified a top percentage, only margin-top will affect the location of your .item object. If instead you positioned it using bottom: 50%, then you'd need margin-bottom -8px to centre it, and margin-top would have no effect.

Margin affects the boundaries of an element in terms of positioning it, either absolutely as in your case, or relative to neighbouring elements. Imagine that margin is the foundations of your element on which it sits. They are typically the same size as it, but can be made larger or smaller on any or all of the four edges.

Your CSS tells the browser to position the top of your element the margin at a point 50% of the way down the page. However, as all elements are not a single pixel, the browser needs to know which part of it to line up 50% of the way down the page. For lining up the top of the element, it uses the top margin. By default this is in line with the top of the element, but you can alter it with CSS.

In your case, top 50% would result in the top of the element starting in the middle of the page. By applying a negative top margin, the browser uses the point 8px into the element from the top (ie the line across the middle of it) as the place to position at 50%.

If you apply a positive margin to the bottom, this extends the line the browser uses to position the bottom out away from the element itself, giving a gap between it and any adjacent element below, or affecting where it is placed absolutely if positioning based on the bottom.

Solution 7 - Css

I wonder if this question has been answered well: how css margins work and why is it that margin-top:-5; is not the same as margin-bottom:5;?

Margin is distance from the surroundings of the element. margin-top says "... distance from surroundings as we measure from top 'side' of the element's 'box' and margin-bottom being the distance from the bottom 'side' of the 'box'". Then margin-top:5; is concerned with the top 'side' perimeter,-5 in that case; anything approaching from top 'side' can overlap top 'side' of element by 5, and margin-bottom:5; means distance between element bottom 'side' and surrounding is 5.

Basically that but affected by float'ed elements and the like: http://www.w3.org/TR/CSS2/box.html#margin-properties.

http://coding.smashingmagazine.com/2009/07/27/the-definitive-guide-to-using-negative-margins/

I stand to be corrected.

Solution 8 - Css

Just to phrase things differently from the great answers above, as that has helped me get an intuitive understanding of negative margins:

A negative margin on an element allows it to eat up the space of its parent container.

Adding a (positive) margin on the bottom doesn't allow the element to do that - it only pushes back whatever element is below.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPhDView Question on Stackoverflow
Solution 1 - CssOlegView Answer on Stackoverflow
Solution 2 - CssAnaView Answer on Stackoverflow
Solution 3 - CssDavid HedlundView Answer on Stackoverflow
Solution 4 - CssRich BradshawView Answer on Stackoverflow
Solution 5 - CssschellmaxView Answer on Stackoverflow
Solution 6 - CssRob TrickeyView Answer on Stackoverflow
Solution 7 - CssMaybeView Answer on Stackoverflow
Solution 8 - CsslogicOnAbstractionsView Answer on Stackoverflow