Basic CSS floating elements behavior - html

I have troubles to understand some very basic css floating rules.
Let's say I have a HTML structure like this:
* {
padding: 0;
margin: 0;
}
div {
width: 200px;
height: 100px;
}
.base {
float: left;
border: 10px solid green;
}
.regular {
height: 50px;
border: 10px solid blue;
}
p {
border: 10px solid red;
}
<div class="base"></div>
<div class="regular"></div>
<p>Some text</p>
Now I don't understand two things:
Why are those two floating elements (div.regular and p) aligned to the left edge of the floated div.base element? I would expect they will be aligned to the right edge so they will be next to the base div element.
Why is the div.base element above all other elements? I would expect it at the bottom since it is before them in the HTML flow.
I would appreciate if you put some light on this for me or give me some resources to understand it.

Both of your questions can be answered by referring to section 9 of the CSS2.1 spec.
Why are those two floating elements (div.regular and p) aligned to the left edge of the floated div.base element? I would expect they will be aligned to the right edge so they will be next to the base div element.
From section 9.5:
Since a float is not in the flow, non-positioned block boxes created before and after the float box flow vertically as if the float did not exist. However, the current and subsequent line boxes created next to the float are shortened as necessary to make room for the margin box of the float.
The line boxes refer to the boxes that actually contain the text. In your example, the p element contains one or more line boxes with the words "Some text". Those line boxes are made to reflow around the float. The block box that is generated by the p itself (which in turn contains those line boxes) is laid out as described in the first sentence, simply because block boxes obey different layout rules from line boxes.
Why is the div.base element above all other elements? I would expect it at the bottom since it is before them in the HTML flow.
From section 9.9, emphasis mine:
Within each stacking context, the following layers are painted in back-to-front order:
the background and borders of the element forming the stacking context.
the child stacking contexts with negative stack levels (most negative first).
the in-flow, non-inline-level, non-positioned descendants.
the non-positioned floats.
the in-flow, inline-level, non-positioned descendants, including inline tables and inline blocks.
the child stacking contexts with stack level 0 and the positioned descendants with stack level 0.
the child stacking contexts with positive stack levels (least positive first).
Item #3 refers to div.regular and p, since they are in the normal flow, they are block-level, and they are non-positioned. #4 refers to div.base, which is floating.
Elements are painted in source order when
you have multiple elements belonging to the same category, for example when you have two floating elements, or
otherwise not mentioned in this list.

If you want to use float you should have a container, after that you should play with position of elements in css file:
.container{
width: 100%px;
height: 100%px;
border-style: dotted;
}
.base {
border: 10px solid green;
}
.regular {
height: 50px;
border: 10px solid blue;
}
p {
border: 10px solid red;
}
<div class="container">
<div class="base">div1</div>
<div class="regular">div2</div>
<p>Some text</p>
</div>

Related

What does a block formatting context actually do?

To preface, I understand how to create a BFC, I am confused on what a BFC actually does.
MDN says that
Elements participating in a BFC use the rules outlined by the CSS Box Model
and the spec says that
In a block formatting context, boxes are laid out one after the other, vertically, beginning at the top of a containing block.
MDN gives the example of a floated div that is out of flow, its height independent from the box parent.
<div class="box">
<div class="float">I am a floated box!</div>
<p>I am content inside the container.</p>
</div>
.box {
background-color: rgb(224, 206, 247);
border: 5px solid rebeccapurple;
/* overflow: hidden; */
}
.float {
float: left;
width: 200px;
height: 150px;
background-color: white;
border:1px solid black;
padding: 10px;
}
And then having the box container ending up filling the height of the floated div. once overflow:hidden is declared which creates a BFC.
.box {
background-color: rgb(224, 206, 247);
border: 5px solid rebeccapurple;
overflow: hidden;
}
How does participating in a BFC end up with this outcome? My thought process:
According to MDN's definition, participating in a BFC makes an element adhere to the box model...but don't all elements, even a floated element, adhere to the box model?
According to the spec's definition, all boxes are laid out vertically, but my-floated div and the p tag are side by side.
It seems that a BFC just makes the container fit all elements participating in its BFC, but if this is the case, then how come using a position: absolute instead of a float: left doesn't provide the same outcome of making the container fit the absolutely positioned element?
e.g.
<div class="box">
<div class="abs">I am an absolutely positioned box!</div>
<p>I am content inside the container.</p>
</div>
.box {
background-color: rgb(224, 206, 247);
border: 5px solid rebeccapurple;
overflow: auto;
position: relative;
height: 50px;
}
.abs {
position: absolute;
top: 250px;
left: 100px;
width: 200px;
height: 150px;
background-color: white;
border:1px solid black;
padding: 10px;
}
Could someone explain what the BFC actually does?
Formatting contexts in general establish a set of rules for the layout of their contents.
The rules for Block formatting contexts are that their in-flow contents are laid out vertically one after the other.
In addition, BFCs have a few side-effects, that exist largely to make the rules established for their contents implementable, such as the way they work in the presence of floats inside or next to them.
Taking your points one by one,
According to MDN's definition, participating in a BFC makes an element adhere to the box model...but don't all elements, even a
floated element, adhere to the box model?
Yes they do. MDN does not actual say that it makes them adhere, just that they do adhere. The MDN statement is rather redundant, but not harmful or incorrect.
According to the spec's definition, all boxes are laid out vertically, but my right-floating div and the p tag are side by side.
This is incorrect. The floating div overlaps the p element. Only the inline content of the p element is side by side with the floating div.
It seems that a BFC just makes the container fit all elements participating in its BFC, but if this is the case, then how come
using a position: absolute instead of a float: left doesn't provide
the same outcome of making the container fit the absolutely
positioned element?
As said above, that BFCs contain their floats is best regarded as a side-effect of the BFC, not really what it does.
Finally, although BFCs provide some isolation of their contents from the outside world, it is not complete. For example, inline-block elements establish BFCs for their contents, but their baselines escape this isolation and are used to vertically align the elements with their inline neighbours.
A BFC is a kind of "isolation" to avoid any interaction of the inside and the outside world of an element. From another part of the MDN you can read:
because an element that establishes a new block formatting context will:
contain internal floats.
exclude external floats.
suppress margin collapsing.
Most of the observation you made (box model, absolute, boxes) aren't related to BFC. Just consider the creation of BFC as a way to encapsulate elements inside (mainly float elements because all the other elements are by default "inside"). Of course, this doesn't apply to absolute/fixed elements because they have their own rule that make them out of the flow.

Two elements (block and inline) show differences when their pseudo selectors have negative margins applied

Yes, text cropping 2020.
Luckily a new CSS prop to deal with this mess is on its way, but before it is widely adopted in all browsers (ie11 no longer) will have to deal with trimming the white space above and bellow the text the traditional hacky way.
So in my examples I have 2 elements - a div (block) and a span (inline). Both have ::before and ::after selectors applied to. In one hand the ::before pseudo-element has a negative value margin-bottom (to pull up the main element itself). On the other hand the ::after pseudo-element has a negative value margin-top (to push it up). There is a differense in the results:
Probably it is because of the formatting context.
Anyone who could shed some light on why in the first example the inner (salmon) container remains "hidden behind" the pseudo-elements and in the second example the inner container overlaps them??
Code: https://codepen.io/pollx/pen/MWyvoXm
.box{
box-sizing: border-box;
background: salmon;
/* border: 2px solid blue; */
}
.box::before,
.box::after {
content: " ";
display: block;
height: 5px;
width: 100%;
}
.box::before {
background: red;
margin-bottom: -8px;
}
.box::after {
background: green;
margin-top: -8px;
}
<div class="box">
Box - div
</div>
<br>
<span class="box">
Box - span
</span>
To understand what is happening you need to refer to the painting order algorithm.
The first case is the easiest one and following the painting order we will first paint the background of the div (step (2)) then the background of the pseudo elements (step (4)) then the content of the div (step (7))
If the element is a block, list-item, or other block equivalent:
background color of element unless it is the root element.
then
For all its in-flow, non-positioned, block-level descendants in tree order: If the element is a block, list-item, or other block equivalent:
background color of element.
Then
Otherwise: first for the element, then for all its in-flow, non-positioned, block-level descendants in tree order: ..
The (7) step is too long but there we will be painting the text. (7.2.1.4.1.1.3 the text.)
The second case is a bit tricky because we are dealing with block elements (our pseudo elements) inside an inline element and we need to consider this part of the specification to understand what's happening
When an inline box contains an in-flow block-level box, the inline box (and its inline ancestors within the same line box) are broken around the block-level box (and any block-level siblings that are consecutive or separated only by collapsible whitespace and/or out-of-flow elements), splitting the inline box into two boxes (even if either side is empty), one on each side of the block-level box(es). The line boxes before the break and after the break are enclosed in anonymous block boxes, and the block-level box becomes a sibling of those anonymous boxes. When such an inline box is affected by relative positioning, any resulting translation also affects the block-level box contained in the inline box.
It's not trivial to understand but to resume, your span element will be splitted into 3 elements. One empty element before the ::before another one between the ::before and ::after (containing the text) and a last empty one after the ::after. The 3 elements will be enclosed in anonymous block boxes to have something like the following:
<anonymous_block><span></span></anonymous_block>
::before
<anonymous_block><span>Box - span</span></anonymous_block>
::after
<anonymous_block><span></span></anonymous_block>
adding border and some padding will make this more visible:
.box{
box-sizing: border-box;
background: salmon;
border: 2px solid blue;
padding:0 5px;
}
.box::before,
.box::after {
content: " ";
display: block;
height: 5px;
width: 100%;
margin:5px 0;
}
.box::before {
background: red;
}
.box::after {
background: green;
}
<span class="box">
Box - span
</span>
Now we get back to our painting border and the difference with our first case is that the background of the span will be painted later because it's not done in the step (4) but the step (7)
Otherwise: first for the element, then for all its in-flow, non-positioned, block-level descendants in tree order:
Otherwise, for each line box of that element:
For each box that is a child of that element, in that line box, in tree order:
background color of element.
So we paint the background of both pseudo elements in the step (4) then the background of the span and it's content in the step (7) making the salomon color on the top and no more behind.

Rendering of HTML elements along the z-axis

I'm trying to get to grips with how html elements are rendered along the z-axis and on the whole it is making sense.
When no element has a z-index, elements are stacked in this order (from bottom to top):
1 - Background and borders of the root element
2 - Descendant blocks in the normal flow, in order of appearance (in HTML)
3 - Descendant positioned elements, in order of appearance (in HTML)
Importantly, different stacking contexts can exist; for example through the use of positioning or from nested elements.
This all makes sense to me. At least I thought it did until I tried putting it into practice.
CSS:
.element-one {
border: 1px solid red;
height: 5rem;
background-color: pink;
}
.child-one {
border: 5px solid red;
margin-top: 150px;
background-color: green;
}
.element-two {
border: 5px solid orange;
height: 25rem;
background: url("https://cdn.pixabay.com/photo/2016/11/29/05/34/new-york-1867569_1280.jpg");
margin-top: -30px;
}
HTML:
<body>
<div class="element-one">I am element one<p class="child-one">Child One</p></div>
<div class="element-two">I am element two</div>
</body>
Fiddle
Why does 'child-one' sit stacked above 'element-two' even though it comes before it in the normal flow (point 2 above). Especially given the fact that 'child-one' is also a nested element. What confuses me further, is that it is not the whole element that is stacked on-top, but just the content, i.e. the red border of 'child-one' is stacked behind 'element-two'.
The answer to this question is not immediately obvious, and even a Google search does not immediately come up with a crystal clear solution.
The answer does indeed lie in the concept of 'stacking context'. Moreover, in how each element is rendered, or painted, within that context.
With some close inspection, the answer can be found in the W3C Specs. Specifically Appendix E.2 "Painting Order". Here you can follow a painting algorithm, which describes the painting order for the descendants of an element generating a stacking context; in my specific example the body tag.
The page given in the question is rendered that way because all in-flow, non-positioned, block-level elements are rendered in a unitary fashion; that is, rather than the entirety of each element beginning rendered 'in-turn', the backgrounds and borders of all the elements within the stacking context are rendered together, as are the contents (the docs refer to this as "atomic" rendering).
For all its in-flow, non-positioned, block-level descendants in tree order: If the element is a block, list-item, or other block equivalent:
background color of element.
background image of element.
border of element.

Do block elements ignore floating elements?

W3C states that:
Since a float is not in the flow, non-positioned block boxes created before and after the float box flow vertically as if the float did not exist. However, the current and subsequent line boxes created next to the float are shortened as necessary to make room for the margin box of the float.
This work as expected with divs:
#a
{
width: 100px;
height: 100px;
background-color: blue;
float: left;
}
#b
{
width: 200px;
height: 200px;
display: block;
border: 1px solid black;
background-color: red;
}
<body>
<div id=a></div>
<div id=b></div>
</body>
Here the red div is block-level element, therefore it's ignoring the floating one. (if I changed red one to display: inline-block it would appear next to floating one)
But, if I apply display: block to an image ,it won't ignore the floating div, Why?
#a
{
width: 100px;
height: 100px;
background-color: blue;
float: left;
}
#b
{
width: 200px;
height: 200px;
display: block;
border: 1px solid black;
}
<body>
<div id=a></div>
<img id=b src="https://www.gravatar.com/avatar/5cb44dcd4ebddfea3ede8c6d46b02795?s=328&d=identicon&r=PG&f=1">
</body>
Several paragraphs after the one you quote, the spec says:
The border box of a table, a block-level replaced element, or an element in the normal flow that establishes a new block formatting context (such as an element with 'overflow' other than 'visible') must not overlap the margin box of any floats in the same block formatting context as the element itself.
Although you've applied display: block to your image, being an image it's a replaced element, and therefore the float cannot intrude into its bounds.
Only non-replaced block boxes that don't establish block formatting contexts and are in the same flow as a float may ignore the float.1 A block-level replaced element is not a block box, because a replaced element cannot be a block container box.2
1 You're probably thinking, that's a ridiculously specific statement, and it is. It's why terms like "block element" are considered extremely vague in CSS parlance. Then again, it doesn't help that CSS itself defines almost equally vague terms like "block box" to specifically refer to boxes that are both block-level boxes and block container boxes.
2 This does imply that "non-replaced block box" is somewhat of a tautology, which reinforces just how ridiculously specific that statement is.
W3C
These are the two things that stuck out to me when viewing the W3C. It's considering it as a line box.
A floated box is shifted to the left or right until its outer edge touches the containing block edge or the outer edge of another float. If there is a line box, the outer top of the floated box is aligned with the top of the current line box.
A line box is next to a float when there exists a vertical position that satisfies all of these four conditions: (a) at or below the top of the line box, (b) at or above the bottom of the line box, (c) below the top margin edge of the float, and (d) above the bottom margin edge of the float.

Baseline shifts for block-level element when no content inside

I'm working through a CSS problem at http://www.codecademy.com/courses/web-beginner-en-jNuXw/0/1?curriculum_id=50579fb998b470000202dc8b (actually, just helping a friend learn HTML/CSS) and came across a curious issue. If you erase the content in any of the <p> tags within a <div>, the div shifts upward. For example, delete the word 'Mom' without deleting the <p>. As best as I can figure out, this is because the element is set to vertical-align: baseline and for some reason the baseline is changing. I just can't figure out exactly why it's changing or what is causing it to change.
To be clear, I'm not asking for help to get the div's to align. That's simply a matter of setting their vertical-align to 'top'. I'm just trying to understand how the document flow is calculated. The specific question is: why does the empty div shift upwards?
DEMO: jsFiddle
UPDATE: Here is a new jsFiddle - http://jsfiddle.net/tonicboy/2DtTw/3/ which removes a lot of rules to boil the problem down to a simplified use case. From this, we can see that when a <p> tag has text in it, the baseline of the parent <div> is set at the baseline of the text. When you remove the text, the baseline of the parent <div> is set to the bottom of the <div>. Why is that?
HTML:
<div class="friend" id="best_friend"><p>Arthur</p></div>
<div class="friend"><p>Batmanuel</p></div>
<div class="friend"><p>Captain Liberty</p></div>
<div class="friend"><p>The City</p></div>
<div class="friend"><p>Justice</p></div>
<div class="family"><p></p></div>
<div class="family"><p>Dad</p></div>
<div class="family"><p>Bro</p></div>
<div class="family"><p>Sis</p></div>
<div class="family"><p>Rex</p></div>
<div class="enemy"><p>Baron Violent</p></div>
<div class="enemy"><p>The Breadmaster</p></div>
<div class="enemy"><p>The Deadly Nose</p></div>
<div class="enemy"><p>Dinosaur Neil</p></div>
<div class="enemy" id="archnemesis"><p>Chairface</p></div>
CSS:
div {
position: relative;
display: inline-block;
height: 100px;
width: 100px;
border-radius: 100%;
border: 2px solid black;
margin-left: 5px;
margin-top: 5px;
text-align: center;
}
div p {
position: relative;
margin-top: 40px;
font-size: 12px;
}
.friend {
border: 2px dashed green;
}
.family {
border: 2px dashed blue;
}
.enemy {
border: 2px dashed red;
}
#best_friend {
border: 4px solid #00C957;
}
#archnemesis {
border: 4px solid #cc0000;
}
I think I've mostly figured out the reason, after digging through W3C specs. Here are three key items from the spec which may explain this behavior:
"Line boxes that contain no text, no preserved white space, no inline elements with non-zero margins, padding, or borders, and no other in-flow content (such as images, inline blocks or inline tables), and do not end with a preserved newline must be treated as zero-height line boxes for the purposes of determining the positions of any elements inside of them, and must be treated as not existing for any other purpose."
When you delete the text, the <p> element is no longer in-flow.
"The baseline of an 'inline-block' is the baseline of its last line box in the normal flow, unless it has either no in-flow line boxes or if its 'overflow' property has a computed value other than 'visible', in which case the baseline is the bottom margin edge."
Because there are no in-flow elements within the parent div, the baseline becomes the bottom margin.
Because the div's are set to display: inline-block, their default vertical alignment is 'baseline'
Because the other div's have in-flow elements (the <p> tags), their baseline is set to the text baseline.
And that is why the empty box's bottom margin aligns with the baseline of the <p> tags in the other div's.
The baseline of the element is shifting because the text inside the <p> determs the baseline height:
In an inline formatting context, boxes are laid out horizontally, one
after the other, beginning at the top of a containing block.
Horizontal margins, borders, and padding are respected between these
boxes. The boxes may be aligned vertically in different ways: their
bottoms or tops may be aligned, or the baselines of text within them
may be aligned.
source: http://www.w3.org/TR/CSS2/visuren.html#block-formatting
The height of each inline-level box in the line box is calculated. For
replaced elements, inline-block elements, and inline-table elements,
this is the height of their margin box; for inline boxes, this is
their 'line-height'.
source: http://www.w3.org/TR/CSS2/visudet.html#line-height
CSS assumes that every font has font metrics that specify a
characteristic height above the baseline and a depth below it. In this
section we use A to mean that height (for a given font at a given
size) and D the depth. We also define AD = A + D, the distance from
the top to the bottom.
source: http://www.w3.org/TR/CSS2/visudet.html#inline-box-height
So with this block being a inline-block and baseline is calculted based on the line-height which is calcuted by different font types. Because this <p> has no font/text the baseline will not be positioned.
place all the line-height: 0; and you will see that the one with no text/font doesn't react like the other does:
jsFiddle
So why are the other two elemets shifting that have text in them?
Well it's because the text excist of two lines of text. The margin of the text is bigger and uses more space, thus the baseline is pushed further