Negative margin breaks float - html

While I was working on something, I noticed a very strange behaviour which I couldn't explain. The only difference between the two scenarios is that the <i>s in the second example have margin-top set to -10px instead of -9px. I use the negative margin to shift all of the boxes top with the same ammount.
main, aside {
width: 100%;
padding: 20px 0 10px;
margin-bottom: 10px;
overflow: hidden;
background: lightblue;
}
main i, aside i {
float: left;
display: block;
width: 10px;
height: 10px;
margin: -9px 0 0 5px;
background: orange;
}
aside i {
margin-top: -10px;
}
<main>
<i></i>
<i></i>
<i></i>
</main>
<aside>
<i></i>
<i></i>
<i></i>
</aside>
With only that tiny little change of the top margin they stack on top of each other instead of next to each other. I can't understand what is causing it. I confirmed this behaviour with Gecko and WebKit based browsers.

I'll try an explanation (or better called, an interpretation) for the abnormal scenario. I can't be sure that what I interpret is correct because the specs have a lot of rules and also the browser implementation may not be completely according to the specs.
The first float box is positioned left most in the parent and shifted 10px upwards (because of the -10px margin). When the rendering searches for a position for the second box, it starts looking at height 0 of the parent and goes from right to left until it encounters another float, but it doesn't because the first float was shifted completely out of the parent, so it goes all the way to the left. If it happens like this, the 9 float positioning rules are still respected (more or less, again it depends on how the developers interpreted some things).
Also consider this disclaimer from the margins section:
Negative values for margin properties are allowed, but there may be
implementation-specific limits.
You should understand from it that negative margins should be used at your own risk.
My recommendation is to give up on the negative top margin because it's, let's say, problematic, and use instead some shifting with position: relative (or don't shift at all).
Ref.:
https://www.w3.org/TR/CSS2/visuren.html#float-position
https://www.w3.org/TR/CSS2/box.html#margin-properties

Related

4 sibling elements with variable heights: how to float/flex 3+4 to right, w/o vertical gaps between 1+2 or 3+4?

I am attempting to turn a CMS-generated page of four sibling elements stacked atop one another into a two-column layout. Here is the simplified generated markup:
<section id="element-container">
<header id="element-1">Text header</header>
<header id="element-2">Text subheader</header>
<div id="element-3">Text validation messages</div>
<form id="element-4">Form fields go here</form>
</section>
I need a left column with #element-1 and #element-2 stacked together, and a right column with #element-3 and #element-4 stacked together.
I cannot change the CMS-generated markup (such as to add more nesting levels of container elements).
In real-world usage, #element-4 will almost always be considerably longer than the total combined height of #element-1 and #element-2, though every so often we need to dump a bunch of text into #element-2, making it taller than #element-4.
A CSS-only solution is called for over something like jQuery manipulation of elements via detach() or wrap() etc, for the sake of avoiding an ugly flash of content being rearranged; and hiding everything until it's manipulated is very much undesirable as well. For our particular application, fast/clean loading is the absolute top priority. However, I'd be able to do some of that type of manipulation to (only) the #element-3 div, as it contains feedback to the user about a failed form validation, so it has display:none and height:0px at page load. So I'd be very happy with a solution that "only" stacks #element-1 and #element-2 directly atop one another at left/top, and #element-4 at right/top—from there I can find a way to deal with #element-3.
I spent all day today playing with solutions like the one in Floating 3rd element right while first 2 go left, which is so close to what I need, but all solutions to questions like these seem to assume fixed heights on the elements, and break when uncertainty is introduced to heights. For example, if you play with the codepen on the accepted answer on that question by making div3 200px high instead of 100px high, you get a big gap between div1 and div2.
Open to float, flexbox, whatever. Thanks very much.
You can pretty easily force a two-column layout by using column-count on your container element. This will make the browser automatically divide up the elements into however many columns you specify based on their heights, without leaving vertical space between them.
In your case, with two columns, this will basically automatically ensure #element-1 is on the left and #element-4 is on the right. The question becomes where do #element-2 and #element-3 fall in the auto layout. If the first two elements are pretty small, element 3 may end up in the left column; or if the first element is quite tall and elements 2, 3, and 4 are small enough, they may end up all together in the right column.
Sounds from your description like we probably don't have to worry too much about element 2 ending up in the right column, so I'll focus on forcing element 3 to the right column. (If the former is a problem, the easiest solution is a min-height on element 2, but you can experiment with different options.)
Since element 3 is display: none to start, one option you can do is to figure out how tall it should be right before you display it, and then absolute position it at the top of the right column while moving element 4 down by the same amount as element 3's height to prevent overlap. I'm using a simple top margin on element 4 in the example below.
In the example below, the height of element 3 is static, so the logic is simple enough, but in your real case you may have to do something like:
run form validation
add error messages to element 3
set element 3 to display: block; visibility: hidden
get the height of element 3
offset element 4 by element 3's height
set element 3 to visibility: visible
document.getElementById('element-4').style.marginTop = `${document.getElementById('element-3').getBoundingClientRect().height}px`;
body {
margin: 0;
}
* {
box-sizing: border-box;
}
#element-container {
column-count: 2;
column-gap: 0px;
position: relative;
}
#element-1, #element-2, #element-4 {
display: inline-block;
width: 100%;
}
#element-3 {
display: block;
}
#element-1 {
background-color: firebrick;
padding: 40px 10px;
}
#element-2 {
background-color: midnightblue;
color: white;
padding: 30px 10px;
}
#element-3 {
background-color: gold;
padding: 10px;
position: absolute;
top: 0;
right: 0;
width: 50%;
}
#element-4 {
background-color: mediumaquamarine;
padding: 160px 10px;
}
<section id="element-container">
<header id="element-1">Text header</header>
<header id="element-2">Text subheader</header>
<div id="element-3">Text validation messages</div>
<form id="element-4">Form fields go here</form>
</section>

How to center a floated list?

This is what my page looks like:
Now when I add another post:
It is aligned to the left, but I want them to be centered.
How can I center them?
#demo {
float: left;
margin: 0 auto;
width: 980px;
list-style: none;
}
#demo li {
background: #fec722;
float: left;
margin: 10px 0 10px 15px;
width: 178px;
}
#demo img {
height: 243px;
margin: 3px;
width: 172px;
}
<ul id="demo">
<li><img src="http://goo.gl/0nSAIH"></li>
<li><img src="http://goo.gl/0nSAIH"></li>
</ul>
(JSFiddle)
All you really need are two lines of code (and you can get rid of all the floats and clearfix divs):
#shelf {
display: flex;
justify-content: center;
}
Revised Fiddle
Benefits of flexbox:
minimal code; very efficient
centering, both vertically and horizontally, is simple and easy
equal height columns are simple and easy
multiple options for aligning flex elements
it's responsive
unlike floats and tables, which offer limited layout capacity because they were never intended for building layouts, flexbox is a modern (CSS3) technique with a broad range of options.
To learn more about flexbox visit:
Methods for Aligning Flex Items
Using CSS flexible boxes ~ MDN
A Complete Guide to Flexbox ~ CSS-Tricks
What the Flexbox?! ~ YouTube video tutorial
Note that flexbox is supported by all major browsers, except IE 8 & 9. Some recent browser versions, such as Safari 8 and IE10, require vendor prefixes. For a quick way to add all the prefixes you need, post your CSS in the left panel here: Autoprefixer. More details in this answer.
You just can't float something to the left and align it to the center at the same time.
What I recommend you here, is to avoid the float, and instead set display: inline-block; to the li's. That, in addition to a text-align: center; to the ul makes the trick.
Here's the jsfiddle updated: https://jsfiddle.net/jormaechea/tm91znz2/5/
Look Here It is not identical, but it's a start.
A few things worth mentioning
ID's must be unique to one element, you had multiple list items with the same ID.
To align an element horizontally, it needs to have a display of block, a set width, and a margin on the left and right of auto. This will not work on floated elements. You can also control inline elements with text-align.
Avoid floating elements about the page. Floats are over used, there are much better options to aligning content.
First, about the floats
Stay as far away from floats as you can. They will cause problems with your layout unless you are experienced in their use. 90% of the time, if there is a problem on a page with floats, the floats are the problem. Instead, you should use display: inline-block, it's the best thing since sliced bread.
Floats can be useful for things like putting an image in a paragraph of text, then allowing the text to flow around the image naturally. Other than that, you can probably find a better way to achieve what you're after.
Next, the spacing
So you have some spacing problems. In general, stay away from margins as much as possible (kind of like floats, but not as bad). Margins add to the box sizing, instead of being included in it, as well as they can do other funky things in different situations. If you have to use a margin, use a margin, but try to avoid them. Instead, use padding where you can. You can utilize a container element, then apply padding to it in order to give the appearance of a margin.
When it comes to inline-block elements, space between the tags in the HTML itself will be rendered as a single space. To get around this, set the font-size of the parent to 0, then reset the font-size on the child elements (the default font-size on all modern browsers is 16 pixels).
Lastly, the alignment
Once you've taken all of the advice above into consideration and applied it to your code, just use text-align: center on the parent, reset it on the children, and you're good to go!
Here, have an example, free of charge
#demo {
text-align: center;
font-size: 0;
list-style: none;
margin: 0;
padding: 0;
}
#demo li {
text-align: left;
font-size: 16px;
display: inline-block;
padding: 10px;
}
#demo img {
height: 243px;
width: 172px;
}
#demo .inner {
background: #fec722;
padding: 3px;
}
<ul id="demo">
<li><div class="inner"><img src="http://goo.gl/0nSAIH"></div></li>
<li><div class="inner"><img src="http://goo.gl/0nSAIH"></div></li>
</ul>
Related
https://developer.mozilla.org/en-US/docs/Web/CSS/display
https://developer.mozilla.org/en-US/docs/Web/CSS/float
https://developer.mozilla.org/en-US/docs/Web/CSS/margin
https://developer.mozilla.org/en-US/docs/Web/CSS/padding
https://developer.mozilla.org/en-US/docs/Web/CSS/text-align
Farewell Floats: The Future of CSS Layout
What You Should Know About Collapsing Margins

Why is there a large gap between sections?

I've been creating a page for the last few days and have had this odd gap between the sections I've used. I have them contained in a div named wrapper but even within the div there is about a line's height gap between the top of the div and the start of the section. Further down the page there are also large gaps between the sections.
I can't seem to find a way to change this without messing with the top-margin but even then that is quite a 'hacky' way of doing it.
Here's the code to show I really haven't done anything (as far as I can tell) to the attributes.
section{
height:10px;
min-height: 400px;
background: rgba(0, 0, 0, 0.5);
border-bottom: 5px solid rgba(0, 0, 0, 0.5);
}
#wrapper{
padding: 10px;
}
Here's the JSFiddle if that helps explain what I mean: http://jsfiddle.net/L6qeyhsv/
I thought it may be the default values of section but that wouldn't explain the gap between the top of section at the top of #wrapper
Thanks
I have updated your fiddle,
You needed to remove the margin reset your <h1> to 0. A good way to test this is by using developer tools, highlighting the element and seeing what default margins and paddings are applied to the element.
Fiddle: https://jsfiddle.net/L6qeyhsv/5/
You have to reset default <h1> margin.
section h1 {
margin: 0;
}
Fiddle: http://jsfiddle.net/L6qeyhsv/3/
A side note, you can't have multiple ids with the same name, they must be unique.
Reference: Element identifiers: the id and class attributes
If by chance you want to avoid removing the margin from your h1 (which honestly you can and should remove and replace with padding either way), you can use a common clearfix hack to solve this:
section:before,
section:after {
content: " ";
display: table;
}
This works by essentially adding "first" and "last" elements which aren't affected by margin collapse.
A longer explanation can be found here.

CSS margin issues when the container does not have a border

More frequently then not I come across this issue. Generally I use padding instead of the margin or some quick solution to fix my problem but I too know this is not the correct fix.
Without going deep into writing my issue, I like create a fiddle for better understanding. So here is my fiddle.
.container-node {
margin: 10px;
background-color: #0f0;
}
.content-node {
margin: 20px;
background-color: #f00;
padding: 5px;
color:#fff;
}
.border {
border:1px solid #00f;
}
The issue that I'm trying to point out is if I've two divs, one inside the other and the inside div is given some margin, it takes the margin value differently if the container is bordered and differently if the container does not have a border.
I appreciate any help or documentation on this. Thanks
http://www.w3.org/TR/CSS2/box.html
Read carefully 8.3.1 Collapsing margins
Two margins are adjoining if and only if:
no line boxes, no clearance, no padding and no border separate them
The best solution of this ptoblem i know is clearfix. Its not giving padding or overflow but similar to it.
Fiddle
.cf:before,
.cf:after {
content: " ";
display: table;
}
.cf:after {
clear: both;
}
.cf {
*zoom: 1;
}
As already pointed out it is a "problem" with collapsing margins. A really good read about this issue can be found here:
http://reference.sitepoint.com/css/collapsingmargins
You could just add a padding of 1px and reduce the margin by 1 like so:
.container-node {
margin: 9px;
background-color: #0f0;
padding: 1px;
}
Applied to your problem:
http://jsfiddle.net/n65bX/1/
The .content-nodes margin doesn't work properly, it doesn't have an element to push from. With the border property you define the contour of the element(Based on the border, the margin can push from there).
To easially fix this, you can add a padding to your .container-node instead of the margin on .content-node:
.container-node {
/*margin: 10px;*/
padding: 20px;
background-color: #0f0;
}
Also you are creating your yellow border with a margin. I would suggest you to use also padding for this on the proper element:
.root-node {
border: 1px solid #bababb;
background: #ff0;
margin: 10px 0;
padding: 10px;
}
with proper i mean to the relevant element. You gave an yellow background to .root-node element, so you should also define the size of that element on that element.
It's far more logic to use it this way :)
When you want to create an inline spacing use padding, if you want it to go outside use margin.
jsFiddle
This might also be usefull: When to use margin vs padding in CSS
Update
So you may ask yourself: why isn't the element(.content-node) pushed away based on the parent(.container-node) element?
Well it's fairly simple why this happens. The margin still pushes the element(.content-node) away, only it's based on the wrong element(it is pushed from the .root-node). This is why your yellow border is bigger as the one with the border.
So why is it pushed at the root element?
To debug it even more; let's only set margin to the left and right of the .content-node:
margin: 0 55px;
jsFiddle
It seems that only the top-margin didn't work. And it indeed seems that the margin is collapsing.
I found this topic about this matter: Why does this CSS margin-top style not work?
So i would suggest to use padding so margins aren't conflicting with each other (paddings can never interact in the same 'flow' with each other).
I will try to explain this the best I can.
In the element containing the "container-node", there is no 'area' for that container to give margin to.
By adding sample text before/after , you will see the margin working. Likewise, if you give the "container-node" a border or even padding, you will then provide that element with something for the "content-node" to position against.

Margin not working with float elements

On my web page, I have a logo and a menu that make up header elements and a hero unit under it.
Now I want to give it some bottom margin so there is enough negative space between
header and hero unit but this bottom margin (100px) is not applying.
Same thing if I try to give top margin from hero unit.
.header {
width: 95%;
margin: 20px auto 100px;
}
Here is my working sample JS BIN
Adding a div under it with:
.someclass {
clear: both;
}
would help. But even easier is:
.header {
width: 95%;
margin: 20px auto 100px;
overflow: hidden;
}
If you add the overflow: hidden; the browser will be forced to calculate the size of the elements within, despite them being floated. When the size is calculated, it also knows where to start the margin-bottom.
One more popular uses of setting overflow, strangely enough, is float clearing. Setting overflow doesn't clear the float at the element, it self-clears. This means that the element with overflow applied (auto or hidden), will extend as large as it needs to encompass child elements inside that are floated (instead of collapsing), assuming that the height isn't declared.
Source
The difference between auto and hidden in this case, is that with hidden, it will hide everything that overflows, when it doesn't have enough room anymore, and with auto, it will create a scrollbar.
EDIT:
Since this question apparently is still active, I'll add the most common way of solving this in the present day:
.header:after {
clear: both;
height: 0;
width: 100%;
content: '';
display: block;
}
This is the same as the first method, but then without having to add another element. This is the way to go if setting overflow is not an option (or even if it is an option, this would be better).
When I first posted this answer, it wasn't an option as it was not supported by IE 6 / 7, which were still broadly used back then.
you could add a clearfix to your header or wrapper tag. This is useful bit of css to include in your file. More about the clearfix can be found here
http://css-tricks.com/snippets/css/clear-fix/
I think it's a problem in your margin attribute order.
If I change your property from: 20px auto 100px; to: 20px 0px 100px 0px then I have bottom space appearing.